Pero para entender mejor qué es un estado de flujo, quizá sea conveniente analizar antes el estado justamente opuesto: lo que Csikszentmihalyi llama entropía psíquica. Un estado de entropía psíquica es aquel en el que no hay ningún orden en la conciencia, en el que los pensamientos aparecen y desaparecen caprichosamente sin que podamos controlarlos. Es lo que ocurre cuando te vas a la cama dándole vueltas a algo que te preocupa. Lo mejor sería olvidarte del asunto para poder dormir tranquilo y mañana, con la cabeza más despejada, ver qué se puede hacer para solucionarlo. Pero es inútil, porque cuando te encuentras muy preocupado, los pensamientos tienen vida propia y no podemos controlarlos.
La entropía psíquica es un estado muy desagradable que suele estar relacionado con otros problemas como la inseguridad, la depresión o la ansiedad y que, de repetirse con frecuencia, puede hacer muy infelices a las personas.
El opuesto a la entropía psíquica es el estado de flujo. Cuando la mente fluye, uno se siente completamente atento y en control de sí mismo, despreocupado y con una agradable sensación de estar haciendo lo correcto. Todo toma sentido y los problemas que nos encontramos parecen apasionantes desafíos a los que nos enfrentamos con gusto, no amenazas hacia nuestro bienestar o seguridad personal.
El estado de flujo ha sido descrito, esencialmente de la misma forma, por personas dedicadas a empresas completamente distintas. Cuando se les pregunta a cirujanos, corredores de fondo, marineros, escritores, escaladores, jugadores de ajedrez o trabajadores de una cadena de montaje cuáles son las condiciones en las que se sienten más felices y realizados, todos describen un estado similar al que Csikszentmihalyi denomina flujo. La actividad en concreto parece no tener demasiada importancia. Algunas personas pueden alcanzar un estado de flujo navegando en solitario alrededor del mundo y otras negociando un contrato o en una reunión social. La actividad que le permite a cada uno alcanzar un estado de flujo depende de su personalidad: lo que a un jugador de ajedrez le parece emocionante puede parecerle mortalmente aburrido a un atleta profesional. Y viceversa.
Aún así, Csikszentmihalyi identificó algunas de las condiciones o características del estado de flujo.
La tarea debe tener unos objetivos claros y la persona que la realiza debe conocerlos perfectamente y tener muy claro que se está acercando a ellos. Cuando te encuentras en un estado de flujo tienes la sensación de que estás haciendo las cosas bien y que conseguirás lo que te propones.
La tarea a realizar debe suponer un desafío para la persona que la realiza. Es difícil entrar en un estado de flujo sentado en el sofá tratando de respirar. Eso es tan fácil que resulta aburrido.
Al mismo tiempo, debe ser una tarea al alcance del que la realiza. Si la tarea es demasiado difícil, la persona tendrá dudas acerca de su capacidad, sentirá ansiedad y esto hará imposible que se alcance un estado de flujo.
El estado de flujo es por su propia naturaleza, algo inestable que requiere un ajuste constante para alcanzar el equilibrio perfecto entre demasiado fácil y demasiado difícil. Cuando empezamos a jugar al tenis, el darle a la bola y pasarla por encima de la red puede parecer todo un desafío. Pero al cabo de poco tiempo, lo que antes nos parecía un desafío se vuelve una tarea trivial y debemos ensayar nuevos golpes o enfrentarnos a rivales más difíciles para no aburrirnos. Buscar el estado de flujo conlleva inevitablemente la mejora de nuestras habilidades y la búsqueda de nuevos desafíos.
Lo más interesante del estado de flujo es el papel que desempeña en la capacidad de muchas personas para ser felices. Diversos estudios parecen indicar que algunos factores como el dinero tienen, en realidad, un papel relativo en nuestra felicidad. El dinero, por ejemplo, es importante cuando tenemos poco -igual que la comida es muy importante cuando te mueres de hambre-, pero cuanto más dinero tenemos menos influye éste en nuestra felicidad. Para los niveles de poder adquisitivo de los que disfrutamos en los países desarrollados, existen factores mucho más importantes que el dinero que determinan nuestra felicidad. En cambio, disfrutar de estados de flujo hace que una persona se sienta más segura de sí misma, menos ansiosa y más feliz.
Tras tantos estudios y discusiones filosóficas, el secreto de la felicidad no parece tan secreto: se trata, tan sólo, de disfrutar con lo que se hace.
]]>Por ejemplo, la métrica más usada, por su utilidad, es el tanto por ciento de cobertura del código: el tanto por ciento de código que se ejecuta al lanzar los test. El problema con esta métrica es que se puede falsear trivialmente como demuestra el siguiente ejemplo:
class Sumador
def self.suma(op1, op2)
return op1 - op2 # Estamos restando, no sumando!
end
end
class TestSuma < Test::Unit::TestCase
def test_suma
Sumador.suma 0, 2
assert(true)
end
end
Esta es una implementación rota de una clase para sumar - que, en realidad, resta- y que sin embargo tiene un 100% de cobertura de código.
Incluso cuando no hay ningún ánimo de hacer trampas, un 100% de cobertura del código no garantiza que la implementación sea correcta. Para cualquier código no trivial es imposible conseguir un 100% de cobertura de las bifurcaciones del código, o probar una función para todos los valores posibles de entrada.
Entonces, ¿qué sentido tiene esforzarnos por conseguir una alta cobertura del código? Muy sencillo: una baja cobertura indica que el código es defectuoso o, mejor dicho, está incompleto. Si tus test cubren un 0% del código -es decir, no hay tests- entonces tienes un problema. Si tienes una cobertura alta, es muy probable que tu código sea de alta calidad y esté poco acoplado.
]]>Pensemos, por ejemplo, en los frameworks de javascript. Existen docenas de ellos: jQuery, Prototype, Yahoo UI, Dojo, ExtJs, GWT, MooTools... Todos son buenos y elegir la mejor opción es algo muy complicado: para encontrarla deberíamos pasar un tiempo considerable evaluando cada uno de ellos, sus ventajas y sus inconvenientes, y como solucionan los problemas que tenemos.
Pero, ¿merece la pena tanto esfuerzo? Probablemente no. Aunque haya un framework que sea inherentemente mejor que los demás, vamos a perder más tiempo buscándolo y evaluando posibles opciones, que si utilizásemos directamente el segundo o el tercer mejor framework, porque, entre los más conocidos todos son suficientemente buenos.
En mi opinión, la mejor solución para este problema es la que toma Ruby on Rails. Rails es Opinionated Software, software con opinión. Cuando creamos una aplicación nueva con Rails, la aplicación viene ya "con las pilas incluidas": ya tiene un framework javascript -prototype- una librería de testing, librería para fixtures , un motor de plantillas, un librería de ORM; la aplicación ya tiene una estructura: los controladores van en este directorio, las vistas en este otro; Rails utiliza Convention Over Configuration para darle nombre a las tablas en la base de datos, a las vistas y a los controladores; todas las tablas utilizan como clave principal un id autonumérico. Nada más empezar, el equipo de Rails ya ha tomado un montón de decisiones difíciles por ti.
Todas estas decisiones son discutibles, pero en la práctica resulta que en la inmensa mayoría de los casos, las decisiones que ha tomado el equipo de rails son suficientemente buenas. Rails es software con opinión, dice: yo hago las cosas así. Si luego quieres cambiar cualquier cosa, puedes hacerlo. Ruby es un lenguaje tan dinámico que eso no suele ser problema: aunque rails venga, por ejemplo, con su ORM -ActiveRecord- o su motor de plantillas -ERB-, se puede sustituir ActiveRecord por DataMapper o ERB por Haml. Yo, por ejemplo, cuando programo con Rails suelo utilizar jQuery en vez de Prototype, el framework javasacript que trae Rails por defecto.
Si lo hago es porque tengo claro lo que quiero y sé lo que estoy haciendo. Pero si no lo tienes claro, Rails ya te da un stack que funciona muy bien y te ahorrará muchas indecisiones y quebraderos de cabeza.
]]>Siempre pensamos que tener muchas opciones disponibles es algo inherentemente bueno, una ventaja en cualquier situación. Tener muchas opciones puede ser algo conveniente en ciertas circunstancias, pero también puede conducir a lo que se conoce como parálisis por análisis.
Para estudiar cómo tomamos decisiones complejas, los investigadores Eldar Shafir y Donald Redelmeie dieron a un grupo de médicos el historial hipotético de un paciente de 67 años que sufría de dolor crónico de cadera. El paciente llevaba años de tratamiento con distintos medicamentos que no le habían solucionado su problema, por lo que los doctores se estaban planteando una operación de sustitución de cadera. Esta operación es muy traumática y tiene una recuperación larga y complicada por lo que sólo está indicada en casos los que otras terapias ya han fracasado.
Sin embargo, justo antes de que el paciente se sometiese a la operación, los doctores descubrieron que había un medicamento que no se había probado con el paciente y que podría ayudarle. Ante este historial, el 47% de los doctores optaron por el procedimiento menos agresivo de recurrir a la cirugía si este último medicamento tampoco surtía efecto.
Sin embargo, cuando los investigadores cambiaron la historia para que no sólo quedase un medicamento por probar, sino dos, sólo el 28% de los doctores se decantaron por probar con los medicamentos.
El sentido común nos dice que hay más posibilidades de que cualquiera de los dos medicamentos ayude al paciente, pero este factor no parece determinante en la decisión de los doctores. ¿Qué está pasando?
Cuando nos enfrentamos a una decisión complicada, cuantas más opciones tengamos, más confuso se vuelve el asunto y más difícil es decidirse. Al final, llegamos a una situación de confusión en la que ninguna opción parece claramente mejor, cuanto más analizamos el asunto más difícil resulta tomar una decisión. Sufrimos lo que se conoce parálisis por análisis, una parálisis que nos impide tomar ninguna decisión. En esa situación, suele ganar la ley de la inercia: lo más fácil es no tomar ninguna decisión y que las cosas sigan como estaban.
]]>En 1998 un grupo de investigadores americanos realizaron una serie de experimentos destinados a medir cómo el ser humano hace uso de su fuerza de voluntad. En los experimentos se citaba a un grupo de estudiantes universitarios para participar en un supuesto estudio sobre la percepción del sabor. Los investigadores pidieron a los estudiantes que se saltaran una comida antes del estudio y que no comieran nada en las tres horas anteriores al experimento para asegurarse de que tenían hambre.
Justo antes de que llegaran los conejillos de indias, los investigadores cocinaron en la sala contigua al estudio una remesa de galletas que inundó el ambiente de aroma a chocolate. Cuando los estudiantes llegaron se les sentó en una sala, llena de ese delicioso aroma a chocolate, en la que había una mesa con dos fuentes: una llena de galletas recién horneadas y la otra llena de rábanos. Los investigadores intentaban poner a prueba la fuerza de voluntad de los sujetos: les pidieron a la mitad que comieran galletas de chocolate y a la otra mitad que comieran rábanos. Además, dejaron solos a los sujetos para favorecer la tentación de comer galletas de chocolate.
En principio, parecía que todo el montaje había sido en vano porque ninguno de los desgraciados comedores de rábanos sucumbió a la tentación y se comió una galleta prohibida. Sin embargo, lo que ocurrió poco después demuestra que la tentación de las galletas sí que dejó huella en los comedores de rábanos.
Después unos minutos saboreando la comida que les había caído en suerte -rábanos o galletas- los investigadores proporcionaron a los estudiantes un par de cuestionarios. Cuando los estudiantes terminaron de responder, los investigadores dieron por finalizado el experimento y dieron paso a un nuevo estudio sobre la capacidad cognitiva de los estudiantes. Este experimento consistía en pedir a los estudiantes que resolvieran varios puzzles complicados, trazando una serie de figuras geométricas sin levantar el lápiz de papel. Les dijeron a los estudiantes que no importaba cuanto tiempo tardasen en resolver el puzzle ni el número de intentos que empleasen.
El único problema es que esos puzzles no tenían solución posible: los investigadores simplemente medían la perseverancia de los sujetos. Y en ese test sí que se observó una gran diferencia entre los comedores de galletas y los comedores de rábanos: los comedores de galletas estuvieron intentando resolver el puzzle una media de 19 minutos, e hicieron por término medio 34 intentos. Sin embargo, los comedores de rábanos sólo emplearon una media de 8 minutos y 19 intentos.
Los investigadores eligieron a los comedores de rábanos al azar, no eran gente menos perseverante o motivada que sus compañeros comedores de galletas. Y, sin embargo, perseveraron mucho menos en resolver el puzzle. ¿Por qué? Por lo que se conoce como agotamiento del ego: Podemos emplear la fuerza de voluntad para resistir nuestros impulsos, pero esta capacidad de esfuerzo es finita y se agota si la forzamos demasiado. Nuestra fuerza de voluntad es finita. Usadla para algo positivo.
]]>Estas discusiones rara vez llegan a un resultado provechoso, y no sólo porque los argumentos utilizados muchas veces no tienen ningún fundamento, sino porque la pregunta -¿qué lenguaje es mejor?- está mal planteada: Los lenguajes no suelen ser mejores o peores por sí mismos, sino solamente más o menos adecuados para ciertas tareas.
Los lenguajes son herramientas. ¿Qué herramienta es mejor, un destornillador o una llave inglesa? Depende de si quieres atornillar un tornillo o enroscar una tuerca. Dice el proverbio chino: al que tiene un martillo todo le parecen clavos. Yo digo que antes de utilizar ese martillo para desenroscar una tuerca, el desarrollador debería plantearse si no habrá alguna herramienta más adecuada para lo que quiere hacer.
Nos guste o no, la realidad de la web nos ha enfrentado con el hecho de que un sólo lenguaje ya no basta. Antiguamente era posible hacer una aplicación de escritorio completa usando sólo C, C++ o Java. En cambio, para hacer una aplicación web medianamente decente, hay que conocer el lenguaje en el que se programe el backend -ya sea ruby, php, java o c#-, pero además es inevitable tener conocimientos de SQL, javascript, css, html... Eso como mínimo. Repito: en el mundo web un sólo lenguaje ya no nos basta.
Y este punto no sólo se aplica a la diferencia entre el backend y el frontend de una aplicación web que, al fin y al cabo, son ámbitos distintos y por lo tanto parece natural que utilicemos lenguajes distintos. También dentro del servidor puede tener sus ventajas que utilicemos varios lenguajes de programación.
Los entornos de ejecución modernos, como la máquina virtual de Java o el CLR de .NET, hacen que los distintos lenguajes puedan convivir y entenderse entre ellos. Por ejemplo, la máquina virtual de Java puede ejecutar código escrito en Java, Ruby, Scala, Groovy, Clojure, Python, PHP, Javascript, Ada, Cobol, etcétera. La lista es larga. Y no sólo eso: yo puedo utilizar JRuby, por ejemplo, para llamar a código escrito en Java o en Scala desde mi programa escrito en Ruby. Gracias a la máquina virtual, las barreras entre lenguajes son cada vez más pequeñas y Ruby puede entenderse perfectamente con Java.
Un ejemplo claro es el framework web Grails. Está pensado para utilizarse en Groovy, pero su núcleo está construido sobre Spring e Hibernate. Es decir, tenemos un framework web escrito en Groovy que se apoya en librerías escritas en Java. Y el resultado funciona muy bien.
En su libro The Productive Programmer, Neal Ford llama a este enfoque programación políglota. ¿Qué es la programación políglota? Muy sencillo: se trata de utilizar en cada momento el lenguaje más adecuado para la tarea que haya que llevar a cabo. El programador políglota es aquel que tiene en su repertorio gran cantidad de herramientas -lenguajes-, conoce bien sus puntos fuertes y sus limitaciones, y sabe qué herramienta hay que utilizar para cada tarea.
]]>Que un Big Mac sepa bien -o no, según el gusto- no depende de la pericia del cocinero. McDonald's ha conseguido destilar la receta del Big Mac en una serie de pasos exactos que no dan lugar a fallo: la carne se compra aquí, se hace durante tantos segundos y los ingredientes de la salsa son estos. Todo está especificado al milímetro para que el cocinero sólo tenga que seguir unos pasos sencillos para hacer un Big Mac. Si mañana el cocinero se va a trabajar a otro sitio, no es ningún problema porque es fácil encontrar a otro que haga exactamente el mismo trabajo. Y los Big Macs seguirán sabiendo igual. En McDonald's cualquiera es prescindible.
A gran parte del público no le gustan las sorpresas y ese el motivo de que McDonald's tenga tanto éxito: puede que la comida no sea gran cosa, pero cuando vas, sabes exactamente lo que te espera. No hay sorpresas.
Las franquicias suelen emplear este modelo de negocio: empaquetar un negocio que funciona y es reproducible y venderlo a cambio de una parte de los beneficios. Es un negocio rentable que ha permitido a empresas como McDonald's crecer hasta el infinito y más allá.
Pero lo que sirve para vender comida rápida no tiene por qué servir en otros campos como la informática. Pensemos, por ejemplo, en Google: Google no se dedica a contratar empleados para realizar un trabajo que podría hacer cualquiera, sino que hace justo lo contrario: contrata a gente muy especial, de la inmensa minoría más brillante y les encarga -o les permite crear- productos revolucionarios. Cualquiera no puede hacer un Gmail o un Google Maps, y si no fuera por el talento de gente como Paul Buchheit -el creador de GMail- o los hermanos Rasmussen -creadores de Google Maps y Google Wave- no existirían estos productos. El éxito de Google depende de que tenga trabajando en sus filas a las mentes más brillantes..
Google y McDonald's son dos casos extremos, pero entre ellos hay un espectro continuo de empresas que valoran más o menos la iniciativa de sus empleados; más parecidas a Google o a McDonald's.
En España, por ejemplo, no es raro ver consultoras que se empeñan en aplicar el modelo McDonald's a la informática. Intentan crear un modelo de negocio basado en productos de baja calidad y en legiones de empleados, a los que pagan poco y forman menos, y que son completamente prescindibles. Dicen: vamos a desarrollar este core para que después las vistas, o los módulos, los jsp o lo que sea, se puedan hacer en una software factory. Es una manera de decir: vamos a hacer un producto que cualquiera pueda desarrollar, vamos a hacer que tus empleados sean prescindibles, vamos a inventar el BigMac-JSP.
Creo que en un campo como la informática, donde ser innovador es una ventaja enorme, esta estrategia tiene las patas muy cortas. Puede servir para crear productos chapuceros que funcionan si no eres demasiado exigente, pero nunca servirá para crear un producto de calidad. Así nunca se creará un producto memorable.
]]>Ese código muchas veces no es chapucero porque el programador no sepa hacerlo mejor, sino porque se ha hecho con prisas y sin preocuparse en que ese código pueda extenderse con facilidad en el futuro. Se ahorra tiempo, por ejemplo, en técnicas como los test unitarios que mejoran considerablemente la calidad del código.
Cuando uno se ve forzado a trabajar con una base de código muy mal escrita, los proyectos acaban tardando mucho más de lo necesario. Lo que podíamos haber hecho en dos días acaba tardando diez. O cuarenta. Todo es mucho más complicado porque tenemos que enfrentarnos a las problemas que nos presenta el código legado.
Para explicar las consecuencias del código hecho con prisas Ward Cunningham acuñó el término deuda tecnológica. Hacer código de mala calidad a toda prisa, es como pedir un crédito: puede que obtengamos un beneficio a corto plazo pero si tenemos que seguir desarrollando ese código, tarde o temprano tendremos que pagar todo el tiempo que nos habíamos ahorrado y los intereses. Refactorizar código mal escrito es siempre más difícil que escribirlo bien desde el principio.
A veces la deuda tecnológica tiene sentido. Puede que tengamos que terminar un producto antes que un competidor, o puede que si no terminamos pronto perdamos una gran oportunidad de negocio. En ese caso, la deuda tecnológica tiene el mismo sentido que la deuda financiera: incurrimos en algo de deuda para obtener unos beneficios tan grandes que compensan de sobra el pago de los intereses.
Sin embargo, la deuda tecnológica, como la financiera, también tiene un aspecto muy peligroso: recurrir a la deuda es una manera muy sencilla de tapar los problemas que tiene nuestro negocio. Entra dinero y crea la ilusión de que el negocio sigue funcionando, aunque en realidad sólo hemos aplazado los problemas al tiempo que los empeoramos.
Con la deuda tecnológica pasa lo mismo. Hacer constantemente chapuzas puede dar la sensación de que los proyectos avanzan. Sin embargo, al poco tiempo acabamos con una maraña de código inmantenible; lo que debería ser muy sencillo se convierte en complicado y los desarrollos avanzan cada vez más despacio.
Es porque todo nuestro esfuerzo se invierte en pagar los intereses de la deuda tecnológica.
]]>Digo en teoría, porque en la práctica esto no suele suceder. Yo mismo he tenido mi buena ración de documentos funcionales, me ha tocado escribirlos y corregirlos, pero nunca los he utilizado para lo que se supone que están hechos: para aclarar mis dudas sobre como debe funcionar un programa.
Los documentos funcionales no parecen muy útiles. Algunos expertos en hacer buen software, como la gente de 37 Signals, incluso piensan que no sirven de nada. Pero, entonces, ¿por qué seguimos haciendo documentos funcionales?
La respuesta, como casi siempre que se reincide en un error claro, no es tanto técnica como psicológica.
A muchos desarrolladores o responsables de proyectos les gustan los documentos de requisitos funcionales porque limitan su responsabilidad. Hacer buen software que contente al cliente es algo complicado; cumplir un documento de requisitos funcionales es algo mucho más sencillo. El documento funcional es una especie de contrato de lo que el desarrollador se compromete a hacer: en vez de comprometerse a hacer un software de calidad que resuelva problemas reales, el desarrollador se compromete sólo a implementar lo que está escrito en el funcional.
Si el producto no funciona bien, o no resuelve los problemas del cliente, el desarrollador se siente eximido de responsabilidad: "Mi programa cumple el funcional -dice-, si querían otra cosa, tenían que haberlo dicho en el documento funcional". La culpa no es mía, sino del cliente, que no ha sabido escribir un buen documento funcional.
Los documentos funcionales son la excusa perfecta para hacer software chapucero y luego echarle la culpa al cliente.
]]>Es una pregunta complicada que no tiene una respuesta sencilla y no es extraño que muchas empresas se equivoquen con su respuesta. Los procesos que habitualmente siguen las grandes empresas para contratar gente son bastante penosos y están condenados al fracaso. Examinarlos nos puede servir, al menos, para descubrir como no se distingue a los buenos desarrolladores.
En las grandes empresas las contrataciones suelen ser responsabilidad del departamento de recursos humanos, es decir gente especializada en contratar trabajadores pero que rara vez tiene conocimientos técnicos. Y eso es lo peor que se puede hacer para encontrar buenos programadores: dejar que sólo gente sin conocimientos técnicos evalúe a los aspirantes.
Alguien sin conocimientos técnicos tiene unos medios muy limitados para saber si un aspirante es adecuado para un puesto. Al final acaba limitándose al sencillo criterio de contar las bolitas del curriculum:
¿Y qué significa eso? Alguien con la cara suficientemente dura, puede "ser experto" en cualquier cosa.
Incluso, aunque el currículum no esté inflado, es muy difícil hacerse una idea del candidato sólo con leerlo. La experiencia y los títulos son indudablemente importantes, pero dicen poco de la dedicación y la capacidad de aprender nuevas cosas que, al paso al que evoluciona la informática, son las características más importantes que debe tener un buen desarrollador.
La experiencia siempre es un grado, pero no es un criterio infalible: tampoco es raro ver a gente que lleva diez años programando sin haber aprovechado ese tiempo. Si alguien lleva diez años haciendo chapuzas, casi mejor que no tuviese esa experiencia, porque acaba tan acostumbrado a hacer chapuzas que no concibe que se pueda desarrollar software de otra manera.
Con los títulos pasa lo mismo: hay quien aprende en cada curso que hace, y hay otros muchos que sólo sacan de provecho un papel para enmarcar y dejar colgado de la pared, y una frase en el currículum.
Entonces, si todos estos métodos son tan deficientes, ¿como podemos distinguir a los buenos desarrolladores?
Para encontrar la respuesta pensemos en que, aunque alguien sin conocimientos técnicos no puede distinguir el código bien escrito del que es una chapuza, un buen desarrollador puede distinguirlos con facilidad. Hablando con un candidato puede hacerse rápidamente una idea del nivel de conocimientos del candidato, de si le interesa lo que hace y si realmente le gusta su trabajo. Si el candidato resuelve alguna tarea práctica es fácil evaluar su capacidad de enfrentarse a los problemas. Y si ha colaborado con proyectos Open Source muchísimo mejor: mirando sus contribuciones puedes hacerte una idea muy aproximada cómo trabaja ese programador.
Al final la respuesta al enigma de cómo se distingue a los buenos programadores es sencilla: los buenos programadores se distinguen entre sí.
]]>La ingeniería del software es una recién nacida: la humanidad lleva miles de años construyendo edificios, barcos y puentes, pero sólo unas décadas construyendo software.
Cuando nació la ingeniería del software se fijó en sus hermanas para descubrir como se hace eso de construir sistemas complejos. Y, básicamente, la ingeniería del software copió los métodos tradicionales en lo que se conoce como Modelo en Cascada.
En el Modelo en Cascada, los ingenieros hacen primero un análisis de los requisitos del proyecto: Si se va a construir un puente, hay que saber qué capacidad de tráfico tiene que atravesar el puente, los vientos, mareas y temblores que debe soportar, etc. Con todos estos requisitos se hace un documento funcional explicando cómo debe funcionar el puente cuando esté acabado. Al documento funcional le sigue un diseño técnico: en una obra serían los planos que modelarían el puente, las instrucciones para construir el puente. Una vez se tiene un plan, hay que construir el puente. Cuando el puente está construido, pasamos a la fase de mantenimiento. Este es, muy resumido, el modelo en cascada.
Este sistema funciona razonablemente bien en las ingenierías tradicionales, pero fracasa estrepitosamente en la ingeniería del software. ¿Por qué? Porque los requisitos y el proceso de construcción del software son de naturaleza completamente distinta al los de la construcción de un puente.
Cuando se construye un puente, todos los requisitos y las funciones que debe cumplir deben estar especificados de antemano. Hay que hacer un plano que detalle al milímetro cómo va a ser el puente y seguirlo al pie de la letra. Una vez se empieza a construir un puente, no podemos cambiar de opinión a la mitad y decir, "no nos habíamos dado cuenta, pero necesitamos que los barcos puedan navegar por debajo del puente. Cámbiame el puente que estas haciendo por un puente levadizo". Por desgracia, una vez hemos puesto los cimientos, el diseño no se puede alterar demasiado.
En cambio, en el desarrollo de software los requisitos cambian constantemente. Y eso no es algo malo, sino que es una ventaja que deben aprovechar los buenos desarrolladores.
En vez de planificar todo de antemano, y seguir el plan al pie de la letra, es bueno permitir cierta flexibilidad para que el proyecto vaya creciendo orgánicamente y se vaya definiendo con el tiempo. Puede que el cliente se dé cuenta de que lo que ha pedido no es exactamente lo que necesita, sino que, en realidad, quiere un producto ligeramente diferente. O muy diferente. Eso está bien: nos hemos dado cuenta y podemos corregirlo antes de que el cliente se quede con un producto que no resuelve sus problemas. Cambiar el diseño a mitad de proyecto hace que el producto final sea más eficiente y, sobre todo, satisfaga mejor las necesidades del cliente.
Muchos proyectos tremendamente exitosos empezaron siendo siendo una cosa y terminaron siendo otra completamente distinta, pero fue precisamente este cambio el que les permitió triunfar. Flickr, por ejemplo, empezó como un juego online, pero los desarrolladores se dieron cuenta de que una inocente funcionalidad que permitía a los jugadores compartir fotos se estaba convirtiendo en la estrella de su servicio. Fueron inteligentes y cambiaron sus requisitos iniciales; acabaron convirtiendo un juego medianamente exitoso, en un servicio para compartir fotos tremendamente exitoso.
Flickr no es un caso único. Blogger nació como un servicio de gestión de proyectos; en sus comienzos, Paypal era una plataforma para hacer micropagos con las Palm Pilot... Puede decirse que la mayoría de los proyectos exitosos no lo son por sus ideas originales, sino por la facilidad que tienen de adaptarse a los cambios y a las nuevas oportunidades.
Pero -estaréis pensando- ¡cambiar los requisitos a mitad de un proyecto tiene un coste muy grande! Eso no se puede hacer. ¿No?
Sí y no. Cambiar los requisitos tiene un coste muy grande cuando se sigue el modelo en cascada, en el que toda la planificación se hace de antemano, y cada fase depende muchísimo de que la anterior se haya hecho bien. En esas circunstancias, cambiar la planificación significa echar por tierra mucho esfuerzo y trabajo.
Pero el modelo en cascada no es la única manera de hacer software.
(Las cascadas son bonitas, pero no para hacer software)
En los últimos diez años han surgido una variedad de metodologías ágiles que apuestan por desarrollar software de forma iterativa: se van añadiendo funcionalidades en iteraciones pequeñas, de unas pocas semanas. Tras cada iteración se evalúan los requisitos para ver si siguen siendo las necesidades reales del cliente, y en consecuencia se planea la próxima iteración. Es natural posponer los problemas hasta que tengamos más datos, y si necesitamos cambiar la planificación, tampoco perdemos tanto trabajo porque, en realidad, tampoco hemos invertido tanto esfuerzo planificando.
En el desarrollo ágil los cambios de requisitos no se ven como un inconveniente, sino como una ventaja potencial. Como dice el Manifiesto Ágil:
Valoramos más la respuesta ante el cambio que el seguir un plan. ... Aceptamos que los requisitos cambien, incluso en etapas tardías del desarrollo. Los procesos Ágiles aprovechan el cambio para proporcionar ventaja competitiva al cliente.]]>
Los encargados de un equipo de programación han reconocido desde hace tiempo una gran diferencia entre la productividad de los buenos programadores y los malos. Pero cuando vimos estas magnitudes medidas, todos nos quedamos asombrados. En uno de sus estudios, Sackman, Erikson, y Grant midieron el rendimiento de un grupo de programadores experimentados. Dentro de este grupo, los ratios entre los mejores y los peores eran de media 10:1 en las medidas de productividad y de un increíble 5:1 para las medidas de rendimiento del programa. (...) Los datos también mostraron que no existe ninguna relación entre la experiencia y el rendimiento, aunque dudo que esto sea siempre cierto. *Frederick P. Brooks, The Mythical Man-Month*
Este hecho es muy importante para las empresas que necesitan programadores ya que un programador, por muy productivo que sea, nunca cobra diez veces más que un compañero. Imaginemos, por ejemplo, que un programador normalito puede cobrar unos 25.000 euros al año, mientras que su compañero, un programador fuera de serie, puede cobrar el doble: 50.000 euros al años. Si el programador fuera de serie puede hacer el trabajo de 10 programadores normalitos, eso quiere decir que contratándolo la empresa gasta más en un sueldo, pero se ahorra otros nueves sueldos. En nuestro ejemplo, el ahorro sería de 200.000 euros al año, o cuatro veces más que el sueldo del programador fuera de serie.
En resumen: los buenos programadores pueden parecer caros pero si hacemos las cuentas, en realidad son un chollo.
]]>El único problema es que ese dato no tiene el menor significado estadístico. Usando la misma lógica podríamos decir que el 77% de los muertos en accidente de tráfico sí usaban el cinturón de seguridad. O que el 45% de los fallecidos había desayunado con café con leche y un 17,8% leche con colacao. Estas cifras no nos dicen nada de la utilidad del cinturón de seguridad, del café con leche o del colacao, para proteger a los conductores.
Una comparación con sentido sería, por ejemplo, decir que sólo el 10% de los conductores no usan cinturón de seguridad, pero que esa cifra se eleva hasta el 23% entre los muertos en accidente de tráfico. De esa manera, sí quedaría demostrado que el cinturón de seguridad salva vidas en la carretera. Pero se ve que la DGT y la mayoría de los medios andan bastante peleados con las matemáticas en general y la estadística en particular.
Tampoco os confundáis sobre el motivo de esta entrada: utilizad el cinturón de seguridad, es muy importante y puede salvaros la vida. Esta entrada no es contra el cinturón de seguridad, sino contra las estadísticas estúpidas, las instituciones que las fabrican y los medios que las repiten.
]]>Es un principio general que puede servir como aproximación en muchos campos y que es muy importante en ingeniería del software, dónde suele ocurrir que el 80% de la funcionalidad del software se puede desarrollar con un 20% del esfuerzo.
Parece una afirmación peregrina, y evidentemente los porcentajes no son exactos, sino que sólo pretenden ser una mera aproximación. Sin embargo, al evaluar cualquier proyecto que conozcas o en el que hayas trabajado, imagina cual sería el coste y el beneficio de hacer sólo las partes más sencillas de la aplicación: obviar los casos complicados, no hacer un sistema de control de errores sofisticado, no validar las entradas de los usuarios y no preocuparse por si una de cada 10 veces la aplicación puede fallar. Así, ciertamente, se puede hacer una aplicación que parece funcionar, digamos un 80% del tiempo, y con un esfuerzo mucho inferior al que costaría desarrollar una aplicación completa.
Otras aplicaciones interesantes del Principio de Pareto a la ingeniería del software podrían ser:
Este principio también explica por qué las aplicaciones que parecen casi terminadas suelen demorarse al final del proyecto, cuando sólo falta por resolver la infinidad de cabos sueltos que siempre se dejan para el final: lo que más cuesta en una aplicación no es construir el esqueleto, sino pulir los detalles.
Pero el principio de Pareto sirve especialmente para minimizar el riesgo que se corre al desarrollar una aplicación y se utiliza sobre todo en las metodologías ágiles de desarrollo de software. Según esta escuela de la ingeniería del software, es muy importante que los desarrolladores tengan cuanto antes feedback de cómo funciona el producto que están desarrollando y si satisface, o no, las necesidades del cliente.
Si desarrollamos primero las partes más complicadas de un producto, sin tener nada que enseñar hasta que todas las piezas estén completas, y esperamos hasta el último momento para desplegarlo y enseñárselo al cliente, nos podemos encontrar con la situación, tan común como desagradable, de que el producto cumple los requisitos funcionales que pidió el cliente, pero no es exactamente lo que el cliente necesita. No obstante, esto no lo saben ni el cliente ni los desarrolladores hasta que ven el software funcionando e intentan probarlo. El cliente acaba insatisfecho con el software que ha comprado y el desarrollador se siente frustrado porque ha trabajo duro para cumplir todos los requisitos funcionales pero el cliente no parece apreciar el software que tanto trabajo le ha costado construir.
En cambio, si desarrollamos primero las partes más útiles del sistema, con un 20% del esfuerzo ya se puede ver si el diseño está bien hecho y si la aplicación es realmente lo que el cliente necesita. Si hay que hacer cambios en el diseño o en los requisitos, cuanto antes se definan esos cambios más útiles serán y menos costará implementarlos.
El principio de Pareto no es una ley matemática exacta y no puede utilizarse como dogma, sino más bien como una regla del sentido común, pero aplicado con moderación puede servir para planificar correctamente un proyecto y evitar el riesgo de malgastar recursos en esfuerzos inútiles.
Como escriben Kent Beck y Martin Fowler en Planning Extreme Programming:
Los programadores de software están acostumbrados a tratar con la regla del 20-80 -el 80% de los beneficios provienen del 20% del trabajo. La programación XP hace uso de está regla -pon el 20% más valioso de la funcionalidad en producción, haz el 20% más valioso del diseño, confía en la regla del 20-80 para postergar la optimización.]]>
¿Como surgió el mito de que Java es lento?
Cuando Java nació en el año 1995, las aplicaciones web aún eran ciencia ficción. En esa época un megabyte de memoria costaba 30$ ( ≈ 30.000 dólares 1GB) y un procesador Pentium a 120Mhz salía a 935$ a precio de mayorista.
El desarrollo se centraba principalmente en aplicaciones de escritorio y los principales rivales de Java eran C y C++. A diferencia de C o C++, el código fuente de Java no se compila directamente a código máquina, sino que se transforma primero en un formato intermedio, el bytecode, que luego es interpretado por la máquina virtual.
Los primeros benchmarks no tardaron en llegar e indicaban claramente que un programa Java se ejecutaba entre 10 y 30 veces más lento que un programa equivalente en C++.
Esto era Java alrededor de 1996, hace catorce años, es decir, una eternidad en el mundo de la informática.
Hoy día las cosas han cambiado mucho. En primer lugar, la propia máquina virtual de Java (la JVM) ha evolucionado pasmosamente hasta convertirse en una pieza de software altamente optimizada.
La máquina virtual de Java cuenta hoy en día con compiladores JIT (Just In Time) que traducen el bytecode a código nativo de la máquina. Además, la JVM también puede monitorizar qué partes del programa se ejecutan más a menudo y optimizar la compilación para el uso real que se le da a la aplicación. Es lo que se conoce como Optimización Adaptativa, y es una ventaja muy importante con la que no cuentan los compiladores tradicionales.
Esas son las dos principales mejoras de las que se ha beneficiado la máquina virtual de Java, pero no las únicas.
El resultado de todos estos cambios, es que Java, a día de hoy es tan rápido o más que C++. Dependiendo de quién haga el benchmark, Java resulta ser un poco más rápido o un poco más lento.
Por ejemplo, el The Computer Language Benchmarks Game es un juego de benchmarks basados en pruebas de cálculo aritmético sencillas donde se comparan distintos lenguajes. En esas pruebas Java, cuando descontamos el tiempo de arranque de la máquina virtual, aparece en los primeros puestos, sólo por detrás de C, C++ y ATS, sacando bastante ventaja a otros lenguajes como C# y barriendo literalmente a los lenguajes dinámicos como Ruby, Python o PHP. Java resulta ser unas 80 veces más rápido que PHP.
No obstante, los benchamarks que miden calculos aritméticos sencillos no se correlacionan demasiado bien con el rendimiento de sistemas complejos, donde la interacción entre distintas partes de la aplicación, las librerías de E/S, o la concurrencia son muy importantes. En benchmarks donde se comparan sistemas complejos, Java se muestra a menudo más rápido que C++. Por ejemplo, algunos benchmarks indican que el servidor web Apache Tomcat (escrito en Java) puede ser más rápido que su primo Apache httpd (escrito en C).
Pero todo esto tiene una importancia relativa, porque el mayor cambio que ha habido en el mundo de Java no ha sucedido dentro de la plataforma, sino fuera de ella. Los principales rivales de Java para crear aplicaciones web ya no son C y C++ sino, más bien, PHP, Ruby o Python. Y Java es mucho más rápido que cualquiera de estas tres alternativas. Y lo que es peor: tampoco importa. Ruby, por ejemplo, es lo suficientemente rápido para hacer aplicaciones web con un buen rendimiento. Como ya he explicado en otra ocasión, el hecho de que Ruby sea más lento que Java no lo hace menos escalable.
Pero, me diréis, "existen muchas aplicaciones en Java leeeentas y pesadas, yo las he visto. Fíjate en Eclipse, me puedo tomar un café mientras arranca." Y es cierto. Pero eso no es culpa de la plataforma Java, sino de los programadores que crearon esos engendros. Durante muchos años, J2EE (el entorno en el que se han desarrollado la mayoría de las aplicaciones Java empresariales) ha sido una plataforma sobrecargada de ornamentos inútiles y pesados. No es de extrañar que una aplicación que usa los EJB 2.0 distribuidos vaya lenta. Pero el fallo está en utilizar EJBs distribuidos, no en el lenguaje de programación ni en la máquina virtual.
Y eso nos lleva a la verdadera razón de por qué ha sobrevivido tanto tiempo la monserga de que Java es lento: es una excusa perfecta para hacer software malo. Los desarrolladores pueden hacer un truño de aplicación, y cuando no funcione bien se encogerán de hombros y dirán: 'qué quieres, no es culpa mía, ya sabes que Java es lento'.
]]>No suele suceder que alguien diga: "he hecho unas pruebas de estrés y he visto que para una concurrencia de 300.000 usuarios haciendo unas 5.000 peticiones por segundo las queries a las tablas de usuarios y productos dan un 85% de fallos. ¿Qué tal si denormalizamos tal campo, añadimos este índice y cacheamos estas peticiones?" No, eso que sería algo concreto, productivo y acertado. En vez de eso, lo que se oye más a menudo son argumentos como: "no podemos desarrollar la aplicación en Ruby (o Java, o lo que sea) porque Ruby (o Java o lo que sea) es lento y no puede escalar. Utilicemos mejor X", dónde X puede ser cualquier tecnología desde PHP (extrañamente, en la imaginación popular PHP es más rápido que Java) hasta C, ensamblador o html estático. Cuanto más obsoleta esté la tecnología, mejor.
Si alguien os suelta algo así, tened claro que vuestro interlocutor no tiene idea de lo que habla y os hará falta mucha pedagogía para intentar convencerlo de lo contrario. El primer paso es concretar qué significa exactamente escalabilidad.
Para que un sistema sea escalable tiene que cumplir tres condiciones:
Tiene que poder adaptarse a un incremento en el número de usuarios.
Tiene que poder adaptarse a un incremento en el tamaño de los datos que maneja.
Tiene que ser mantenible.
Cuanto más fácil sea cumplir estas tres condiciones, más escalable será el sistema.
Y ahora, llegamos al punto que más confusión crea al hablar de escalabilidad: la escalabilidad no es lo mismo que el rendimiento del sistema. El rendimiento (performance) de la aplicación se define como la velocidad a la que procesa las peticiones. Pues bien, en general se puede mejorar fácilmente el rendimiento de las aplicaciones que son escalables, pero lo contrario no es cierto: una aplicación puede rendir muy bien pero no ser escalable.
Para entender este punto concretemos con un par de ejemplos.
Primero, imaginemos el siguiente servlet:
```java public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Thread.sleep(4000); //Nos echamos una siesta antes de responder
PrintWriter out = response.getWriter();
out.println("Hello, world, I'm a slow but scalable servlet");
out.close();
} ```
Esta aplicación tiene un rendimiento muy malo: tarda 4 segundos en procesar una petición en la que literalmente no hace nada. Sin embargo, es una aplicación escalable según el criterio que usamos anteriormente: la aplicación no maneja datos por lo que eso no puede ser un problema. Y el mantenimiento de 7 líneas de código no debería ser una pesadilla. Por último, la aplicación puede atender a un número indefinido de usuarios simplemente añadiendo más máquinas. Si una máquina puede atender 1.000 usuarios, al añadir una segunda máquina se prodría atender a 2.000, por lo que se dice que tiene una escalabilidad lineal. La escalabilidad lineal es la mejor que se puede conseguir en sistemas grandes.
Ahora, en cambio, imaginemos una aplicación web con un alto rendimiento en el que el tiempo de respuesta medio es de 40ms, lo cual está francamente bien. No obstante, la aplicación guarda su estado en memoria de forma que cuando aumenta el tráfico del sitio no podemos simplemente añadir otra máquina al sistema porque los cambios aplicados a un nodo no se propagarán a otro. Esta aplicación tiene muy buen rendimiento pero no es escalable según nuestra definición.
Entonces, si el rendimiento no es el factor crucial que determina la escalabilidad, ¿qué hace escalable a una aplicación web? ¿Cuales son las estrategias más sencillas para escalar una aplicación web? Por los ejemplos anteriores ya podemos intuir que la escalabilidad depende más del diseño de la aplicación que de las tecnologías usadas para implementarla.
La estrategia más sencilla para escalar una aplicación web es simplemente comprar hardware mejor y más caro. Si nuestro hardware actual puede servir 100 peticiones por segundo, cuando alcancemos ese límite podemos actualizar la máquina a una con más potencia y así servir más peticiones. Esto se conoce como escalabilidad vertical.
La sabiduría popular nos dice que la escalabilidad vertical tiene un límite: llegados a cierto punto, simplemente no hay máquinas más caras, por lo que no se puede seguir escalando verticalmente. Además, el precio de las máquinas suele aumentar exponencialmente con su potencia, por lo que la escalabilidad vertical suele ser una opción cara.
La ventaja de la escalabilidad vertical es su sencillez: no hay que hacer ningún cambio en la aplicación, simplemente gastarse el dinero en una máquina más grande. Y, además, la escalabilidad vertical cuenta con la ayuda de la ley de Moore: para un precio fijo, la máquina que puedes comprar será cada año más potente. Si con tus máquinas actuales puedes dar servicio a 50.000 usuarios y no planeas superar este límite hasta dentro de al menos un par de años, puedes limitarte a esperar con lo que tienes. Cuando pasen los dos años puedes comprar máquinas más potentes por el mismo precio que te costaron las que tienes ahora.
Aunque parezca una estrategia demasiado sencilla para dar resultado, lo cierto es que puede funcionar muy bien. Los chicos de 37 Signals, que siempre se distinguen por encontrar soluciones sencillas a los problemas complejos, la emplean satisfactoriamente para escalar su base de datos.
Para aplicaciones realmente muy grandes, sin embargo, la escalabilidad vertical no es una opción, por lo que los gigantes de la web como Facebook, Google o Flickr, se decantan principalmente por la escalabilidad horizontal. La escalabilidad horizontal consiste en añadir más máquinas a la aplicación, aumentando su número aunque no necesariamente su potencia.
El mejor ejemplo de este paradigma es la arquitectura de Google. La plataforma en la que corre el famoso buscador está compuesta de decenas de miles de máquinas de hardware utilitario. Entre tanto hardware barato es normal que cada día decenas de máquinas dejen de funcionar por problemas de hardware, pero eso no supone un problema grave: el sistema es capaz de redirigir automáticamente el tráfico de un nodo caído a los nodos sanos y seguir funcionando como si tal cosa. Para aumentar su capacidad, Google sólo tiene que comprar más máquinas y ponerlas a funcionar.
Viendo como funciona Google, la escalabilidad horizontal parece no tener límites, pero ¿qué características debe tener una aplicación para poder escalar horizontalmente?
Para que las aplicaciones puedan escalar horizontalmente se suele utilizar una arquitectura shared nothing. En una arquitectura shared nothing cada uno de los nodos de la aplicación son independientes, autocontenidos y autosuficientes. Como cada nodo puede trabajar por sí solo sin depender de sistemas externos, al aumentar la demanda se pueden añadir tantos nodos nuevos como sean necesarios para cubrirla. Gracias a los servicios en la nube como Amazon EC2 o el Google App Engine el coste de añadir nuevos nodos es cada vez más bajo. Simplemente se crea una imagen del disco duro del sistema con la aplicación y se programa para que arranque automáticamente. Si la afluencia de público aumenta considerablemente se pueden arrancar nuevas instancias para cubrir la demanda.
Una característica importante de la arquitectura shared nothing es que el estado de la aplicación no se guarda en los nodos individuales sino que es compartida por todo el sistema. De esta forma, si algún nodo falla no se pierde información y el sistema puede seguir funcionando. Esta es la estrategia que usa Google para tener una plataforma de proporciones titánicas.
Las aplicaciones web bien diseñadas suelen tener una arquitectura casi shared nothing. Simplificando, algo parecido a esto:
Si los nodos no guardan ningún estado propio, la capa de aplicación de esta arquitectura se puede escalar horizontalmente de forma trivial: sólo hay que añadir más apaches o servidores de aplicación. ¿Pero qué pasa con la base de datos?
La base datos es el punto compartido por todo el sistema, la única parte que se aleja de una arquitectura shared nothing y, por lo tanto, suele ser el principal punto de contención de una aplicación web y la parte más difícil de escalar. Eso no quiere decir que no existan técnicas para escalar una base de datos, las hay y muy eficientes como el sharding o la replicación. Sitios como Flickr o eBay funcionan perfectamente con sus bases de datos MySQL y si a Flickr se basta con MySQL, eso quiere decir que MySQL se puede escalar muchíiiisismo.
Otros gigantes de la Web, sin embargo, no han tenido tanta paciencia con los problemas de escalabilidad de las bases de datos tradicionales. El primero en romper el molde con un diseño innovador fue Google con su BigTable. Le han seguido otros como Amazon con su Dynamo, o Facebook con Cassandra. Todos estos proyectos se caracterizan por abandonar la tradicional base de datos basada en SQL en favor de un diseño de base de datos distribuida de alta escalabilidad.
Hasta hace unos años, sólo las empresas como Google con una capacidad de desarrollo e investigación bestiales podían permitirse el lujo de desarrollar una base de datos no relacional. Sin embargo, hoy día, hay cada vez más bases de datos no relacionales al alcance del usuario final. Entre las opciones Open Source tenemos, por ejemplo, Cassandra, MongoDB, CouchDB, Project Voldemort, Redis, etc. Estos proyectos han nacido en los últimos años y se agrupan en lo que se conoce como el movimiento NoSQL. Las bases de datos NoSQL ya se pueden usar en proyectos grandes y pequeños sin demasiada complicación y en el futuro prometen ser la respuesta a los problemas de escalabilidad de la capa de persistencia en aplicaciones web.
Todo este rollo sobre escalabilidad puede parecer más complejo de lo que es. En realidad, creo que con tener unos conceptos básicos es suficiente, aunque si quieres aprender el tema en profundidad, te recomiendo encarecidamente que leas Building Scalable Web Sites de Cal Henderson. Cal Henderson ha sido el ingeniero encargado de escalar la plataforma de Flickr, así que ten por seguro que habla con conocimiento de causa. La mayoría de los conceptos de este artículo están sacados de ese libro. Otra fuente de información muy interesante, con multitud de ejemplos prácticos, es el sitio web High Scalability.
Recordemos también que, según nuestra definición, para que una aplicación escale debe ser mantenible y, en la inmensa mayoría de los casos lo que suele hacer a las aplicaciones inmantenibles son los malos diseños y el código mal escrito, no las avalanchas de usuarios. Un buen diseño es la mejor garantía de que tu aplicación podrá escalar bien, no el que utilices C o ensamblador para procesar las peticiones.
Sobre todo, espero que este artículo sirva para que te puedas ahorrar alguna de esas discusiones sobre escalabilidad tan poco productivas. Ten en cuenta, que siempre es mejor no optimizar antes de tiempo. Primero céntrate en hacer una aplicación que funcione y sea sencilla y elegante. Siguiendo algunas pautas sencillas como hacer que cada nodo sea autocontenido e independiente de los demás no va a haber ningún problema grave.
Una vez la aplicación esté construida y funcione bien, entonces, si quieres mejorar el rendimiento (que no la escalabilidad) haz pruebas de estrés y busca los verdaderos cuellos de botella de la aplicación. Seguramente te sorprendan. Luego, teniendo datos reales de qué hace la aplicación y qué operaciones son más costosas, entonces, puedes dedicarte a mejorar estos puntos concretos.
Como escribió Donald Knuth:
Los programadores desperdician enormes cantidades de tiempo preocupándose por la velocidad de partes de sus programas, y esos intentos de ser eficientes en realidad tienen un impacto muy negativo en el mantenimiento y la depuración del software. Deberíamos olvidarnos de las pequeñas eficiencias, digamos el 97% del tiempo: la optimización prematura es la raíz de todos los males.]]>
Sin embargo, el software lioso y mal diseñado, en el que todo es más complicado de lo que debería, también existe. ¡Y tanto qué existe! Cualquier desarrollador conoce al menos una aplicación compleja y confusa, un verdadero lío inmantenible. En cambio, lo contrario es una rareza desconocida: una aplicación que peque de ser más sencilla de lo que debería. ¿Quién ha visto algo así?
Si los desarrolladores de software aspiran a la sencillez, ¿qué es lo que acaba convirtiendo al software en un embrollo pegajoso y sucio? ¿Qué pasa durante el proceso de desarrollo de software para que el hermoso y sencillo diseño inicial acabe convertido en un nudo gordiano? ¿Cuales son los pasos que desencadenan la catástrofe?
Para entenderlo, podemos fijarnos en otros campos donde la calidad del producto se degrada imperceptiblemente.
Imaginemos, por ejemplo, una empresa de reparto a domicilio de pizzas. La empresa hace unas pizzas de muy buena calidad que gustan al público y aumenta cada año sus beneficios hasta que toca un techo que es difícil superar (simplemente, porque la mayoría de sus clientes potenciales ya están comprando el producto). Entonces puede surgir algún ejecutivo listillo -y siempre aparecerá un ejecutivo listillo- que, armado de gráficos con colores llamativos y de un powerpoint con muchos puntos con bolitas, exponga algo como lo siguiente:
"Hemos hecho un estudio de mercado y hemos comprobado que podemos hacer las pizzas medio centímetro más pequeñas sin que los consumidores noten nada. Como nuestra empresa vende tropecientos millones de pizzas al año, el ahorro en los materias primas y costes de producción nos puede generar unos beneficios de un porrón y medio de millones al año".
Y el ejecutivo listillo puede que tenga razón: una diferencia de medio centímetro no es nada tan grave como para que el consumidor deje de comprar la pizza. Sin embargo, al seguir este camino se abre un precedente peligroso: dentro de unos meses, otro ejecutivo listillo, celoso del primer ejecutivo listillo, propondrá utilizar un queso más barato y así ahorrar otro puñado de millones. También se pueden abaratar los precios de cada uno de los demás ingredientes, y reducir el tiempo que se tarda en hacer una pizza, simplemente amasando menos la masa; ahorrar un poco más en las condiciones laborales de los trabajadores, aunque eso haga que estén menos contentos y rindan menos y trabajen peor. Y unos meses más tarde se le puede quitar otro centímetro al tamaño de la pizza.
Son cambios pequeños que, por separado, no tendrían demasiada repercusión en la calidad de la pizza. Pero si los juntamos todos, al final obtenemos una verdadera porquería de pizza. Pequeños cambios que individualmente no son importantes, al sumarse pueden crear una gran diferencia.
Con el desarrollo de software ocurre lo mismo. Nadie quiere hacer un sistema complicado, pero invariablemente el desarrollador quiere mejorar su programa y va introduciendo pequeñas complejidades en el código. Un poquito aquí y un poquito allá. Al fin y al cabo, no son cosas tan complejas, se dice, no pueden hacer demasiado daño. Pero sumando docenas de pequeñas complejidades se puede obtener un sistema muy complicado. Y una vez que el sistema se ha complicado, volver atrás y simplificarlo es mucho más difícil de lo que hubiera sido hacerlo sencillo desde el principio.
La única manera de evitar que el software acabe siendo un lío monumental es siendo muy exigente con cualquier cosa que complique el código. ¿Realmente necesita el programa esta funcionalidad? Un error al que tenemos tendencia los desarrolladores es solucionar problemas que no existen. Hacer un software que podría resolver casos muy generales, que en teoría podrían presentarse, pero que en la práctica nunca se presentan. Y, sin embargo, todo este código que resuelve casos imaginarios tiene un coste en complejidad.
El consejo más sencillo que se puede seguir al respecto es centrarse siempre en resolver problemas reales. Como se suele decir: no code is simpler than no code.
]]>Si creemos que nuestro sistema funciona razonablemente bien, podemos averiguarlo viendo lo que sucede en el Parlamento cada vez que se discuten estas cuestiones. En teoría, los ciudadanos eligen cada cuatro años a diputados y senadores para que los representen en las Cortes. El trabajo de estos parlamentarios es representar al pueblo soberano que los ha elegido y cumplir su voluntad libremente expresada en las urnas. En teoría, lo que sucede en las Cortes debería ser un fiel reflejo de la voluntad de los ciudadanos que deambulan por las calles.
Esto es la teoría. En la práctica los ciudadanos percibimos que la situación es muy diferente. Sentimos cada vez más desconfianza de la clase política, hasta el punto de considerarla uno de los problemas más graves que hay en España. No nos sentimos fielmente representados por nuestros políticos. Vemos como políticos mandan y a los ciudadanos nos toca obedecer, cuando, en un sistema democrático debería ser justo al revés. El pueblo es soberano, los ciudadanos somos los que mandamos, y a los políticos les toca el trabajo de representar esa voluntad soberana.
Nuestro sistema político funciona tan mal que no respeta ni los mismos principios en los que se sostiene. ¿Y qué podemos hacer nosotros?
De estas cuestiones surgió la idea de Wasamblea.org. Wasamblea es un Parlamento Virtual donde los ciudadanos pueden proponer, discutir y votar cualquier medida. Espero que Wasamblea llegue a reflejar fielmente de la voluntad de los ciudadanos. Así si, por poner un ejemplo hipotético, las Cortes aprueban una Ley de Economía Sostenible que facilita el cierre de páginas web sin ninguna intervención judicial, al menos sabremos que no se trata de una decisión verdaderamente democrática, sino de una imposición más de nuestra clase política.
Wasamblea.org queda abierto a partir de hoy para el público en general. El sistema está recién salido del horno, por lo que puede que haya algunas cosas que no funcionan todavía. Algunas funcionalidades se han quedado para más adelante. (Los informáticos usamos esta excusa tan a menudo que le hemos puesto un nombre: Wasamblea está aún en fase Beta).
Espero que con la colaboración de todos podamos ir mejorando hasta hacerlo un sitio en el que cualquier demócrata se sienta representado, que los ciudadanos se hagan oír y que los políticos sepan escuchar. Que sea un pequeño pasito para hacer más democrático el mundo.
Señores, pasen y vean. ¡Espero que les guste!
]]>Puede que las críticas más recurrentes sean estas:
Entiendo estas críticas y creo que pueden tener algún fundamento, pero no creo que sean escollos insalvables. La situación me recuerda a lo que ocurrió con la Wikipedia. ¿Quién se puede creer que unos usuarios anónimos trabajando altruistamente puedan crear una enciclopedia que rivalice e incluso supere a la mismísima Enciclopedia Britannica? Es una idea tan loca, que de no estar ahí la Wikipedia, vivita y coleando al alcance de dos clicks, sería increíble. Desde su nacimiento muchos agoreros predijeron que la Wikipedia fallaría miserablemente en unos pocos años.
Sin duda, una empresa como la Wikipedia ha tenido que superar muchos obstáculos. Si cualquiera puede editar una entrada, ¿qué impedirá que algún vándalo introduzca información falsa o manipule la que ya existe con algún oscuro interés, o simplemente por puro cachondeo?. Ese es un problema que aún tiene la Wikipedia, pero aún así, los problemas son las excepciones, no la norma, y el contenido correcto de la Enciclopedia Libre es infinitamente mayor, y compensa con creces errores puntuales.
Además, la Wikipedia ha ido desarrollando con el tiempo mecanismos de control para paliar sus debilidades. A pesar de problemas y agorereos, la Wikipedia se mueve.
Los problemas a los que se enfrenta una democracia directa también parecen bastante serios, así que tendremos que analizar si son escollos insalvables, o meras dificultades a las que habrá que buscar una solución.
La primera cuestión -el pueblo es demasiado estúpido e ignorante para gobernarse a sí mismo- no es una crítica a la democracia directa en particular, sino a la misma esencia de la Democracia. Democracia no significa el derecho a meter una papeleta en una urna cada cuatro años; la democracia es el Gobierno del Pueblo.
Esta idea no es nada nueva sino que, más bien, es tan antigua como la misma democracia. Los propios filósofos griegos, en general, no eran demasiado partidarios de la democracia. Platón, en su libro La República desdeña la democracia, que considera demasiado propensa a caer en la demagogia y postula, en cambio, un sistema en el que gobiernen los mejores -la aristocracia- asombrosamente parecido a los regímenes totalitarios que hemos visto en el siglo XX y que sólo han traído miseria y esclavitud a la humanidad.
Aritóteles, por su parte, dejó escrito:
Cuanto más democrática se vuelve una democracia, más tiende a ser gobernada por la plebe, degenerando en tiranía.
A pesar de todo, la democracia continúa moviéndose, expandiéndose cada vez por más sitios, como una pandemia de leve dolor de cabeza. Han pasado muchos siglos desde la antigua Grecia y han ido apareciendo y desapareciendo en la faz de la tierra formas de gobierno de toda índole. Y una cosa parece clara: ninguna forma de gobierno garantiza mayor prosperidad y libertad que un Estado democrático y de derecho. La democracia puede tener muchos inconvenientes, pero aún así, es el sistema que mejor funciona. Como dijo Churchill,
Muchas formas de Gobierno se han probado en este mundo de pecado y congoja. Nadie pretende que la democracia es perfecta u omnisapiente. De hecho, se dice que la democracia es la peor forma de gobierno, exceptuando todas esas otras formas que se han probado alguna vez.
Quizá sus detractores tengan razón y la democracia real -un gobierno del pueblo- sea algo peligroso, y por lo tanto nos tengamos que contentar con una variante light, en la que el Pueblo manda una vez cada cuatro años, y el resto del tiempo deja hacer a los políticos. Pero, entonces, tendremos que plantearnos cual es la esencia de nuestro sistema político y buscarle un buen nombre, porque, desde luego, eso no es en rigor una Democracia.
Como es tarde y este post ya se alarga bastante, dejo la discusión de los demás problemas para futuras entradas.
]]>La primera democracia conocida, la de la antigua Atenas, era una democracia directa. Los propios ciudadanos se reunían en la colina de Pnyx para elegir los cargos públicos y tomar las decisiones que afectaban a la Ciudad Estado. Cada ciudadano tenía su propia voz y su propio voto.
Después de la Antigua Grecia la democracia desapareció de la historia para aparecer posteriormente en Suiza, Reino Unido, Estados Unidos, Francia, etc. Hoy en día la mayoría de los países del mundo se consideran a sí mismos democráticos, y muchos lo son en cierta medida.
Sin embargo, en su reaparición en la Edad Moderna, la democracia se encontró con un problema logístico: Es físicamente imposible que todos los ciudadanos de un estado moderno se reúnan en una asamblea para discutir y votar las decisiones y leyes públicas. Los estados acabaron reemplazando la democracia directa por un sustituto descafeinado llamado democracia representativa. En la democracia representativa los ciudadanos siguen siendo soberanos, pero en vez discutir y votar cada cuestión directamente, eligen a unos representantes, los políticos electos, para que voten y discutan por ellos.
En teoría, en una democracia representativa los políticos no están para mandar sino para representar a los ciudadanos soberanos.
Y digo en teoría, porque en la práctica los ciudadanos no suelen verse identificados con la clase política y con sus intereses.
Los políticos temen perder el apoyo ciudadano, pero en el ínterin entre elección y elección se sienten autorizados a hacer lo que les dé la gana sin volver a contar con la ciudadanía.
El resultado es que la ciudadanía se siente cada vez más ajena a la vida política.
La cuestión es que nos encontramos en el comienzo de una nueva era, donde todos estos problemas dejan de tener sentido. Gracias a Internet y a los modernos medios de comunicación las antiguas limitaciones que impedían llevar a cabo una democracia directa ya no existen. Internet hace posible que los ciudadanos puedan expresar su opinión y votar sobre cualquier asunto público que les afecte desde su propia casa.
La pregunta que deberíamos hacernos es: ¿Ha llegado el momento de probar una Democracia Directa?
]]>