Por qué no uso productos Apple

En un mundo en el que el iPhone es el modelo de teléfono más vendido y parece que una inmensa cantidad de desarrolladores de software utiliza ordenadores Apple, a veces uno se siente necesitado de justificar por qué no usa ningún producto de esta compañía. Aquí va, para facilidad de referencia.

La censura no mola

Apple desea controlar lo que el usuario de sus dispositivos puede ver y no ver. El ejemplo más claro es la prohibición de pornografía en la App Store. Si bien es su tienda y pueden poner sus reglas, y puede parecer una política respetable, sigue siendo censura y control. Me parece perfecto que se etiqueten los contenidos que pueden ser malignos (obviamente no digo que la industria pornográfica sea un nido de virtud, más bien al contrario) e incluso los que puedan ofender la sensibilidad de unos pocos- y incluso creo que es levemente defendible que el responsable de un menor pueda intentar bloquear el acceso de ciertos materiales a éste [aunque personalmente no esté de acuerdo], pero estoy firmemente opuesto a que una empresa intente restringir lo que puedo hacer con su dispositivo.

Entiendo que los dispositivos iOS disponen de navegadores que hacen que estas restricciones sean más bien un brindis al sol, pero temo que Apple no haya extendido estas restricciones a la totalidad del dispositivo no por falta de ganas sino por la imposibilidad de hacerlo.

Si bien los inicios de la censura suelen ser por motivos loables, el uso de ella es incorrecto de por sí por más de acuerdo que podemos estar con la indeseabilidad del material bloqueado, ya que la voluntad de usar la censura se extiende fácilmente a otros materiales cuya indeseabilidad es mucho más relativa o incluso incorrecta. El hecho de que dispongamos alternativas que nos permitan sortear esta censura tampoco las justifica- la censura es malvada y la única manera de que avance es cortándola de raíz antes de que sea inevitable.

La libertad de desarrollo

Como desarrollador de software, creo que los dispositivos programables son uno de los grandes avances de la humanidad, quizá no a la altura del agua corriente o la higiene, pero sin duda con el potencial de hacer del mundo un lugar mejor. Recuerdo con cariño el Commodore 64 de mi infancia que arrancaba directamente a un interprete de BASIC y que permitía distribuir el software que uno mismo desarrollaba grabándolo en una cinta de cassette de coste mínimo.

Si bien los ordenadores Apple con OS X hasta vienen con herramientas de desarrollo incluidas, la punta de lanza de Apple son sus dispositivos más vendidos, los que usan iOS donde la situación de desarrollo es totalmente diferente. Para desplegar las aplicaciones que desarrollamos sobre hasta 100 iPhones, debemos ser desarrolladores registrados por Apple, lo que tiene un coste y requiere aprobación (dudo mucho que rechacen muchas solicitudes, pero aún así, pueden hacerlo). Si queremos que use nuestro software más de 100 personas, debemos pasar inevitablemente por la App Store y que Apple examine y apruebe nuestra aplicación.

Es decir, no podemos desarrollar software para iPhone y que sea usado por más de 100 personas si este software no es del agrado de Apple- según unos criterios que además son poco transparentes- para lo cual deben tener acceso completo a él.

Esta restricción de la libertad de desarrollo de software me parece completamente draconiana e inaceptable- y extremadamente dañina si se extendiese, por lo cuál una vez más creo que debe ser cortada de raíz.

Apple ya tiene suficiente dinero

Apple gana mucho dinero. Muchísimo. Tiene un margen comercial más amplio que sus competidores. A igualdad de condiciones, si la empresa A tiene un margen del 15% y la empresa B tiene un margen del 30%, voy a comprar el producto de la empresa A. Si bien la eficiencia en la producción de Apple muchas veces significa que los precios de Apple son competitivos (durante mucho tiempo, el Macbook Air de 13″ era imbatible en calidad/precio, y sigue dando mucha guerra), en muchas ocasiones los precios de Apple coloca a sus productos en el segmento de lujo que yo no puedo justificar.

Apariencia sobre utilidad

Con la famosa batería del iPhone, el cargador del Magic Mouse y el lapiz del iPad Pro, últimamente se hace mucho cachondeo sobre la supuesta caída en picado del diseño de Apple. La verdad, no creo que sea algo nuevo.

Hace mucho, mucho tiempo que me compré mi primer producto Apple, un Mighty Mouse  que en vez de ruedecilla tenía una bolita que permitía hacer scroll en dos dimensiones, una idea que me intrigaba sobremanera. Sin embargo, al cabo de muy poco tiempo descubrí que el mecanismo de la bolita acumulaba suciedad sin que hubiese un mecanismo razonable para limpiarla, con lo que el ratón murió prematuramente. Una idea cojonuda pero mal ejecutada.

Veo en los productos de Apple pantallas ultrabrillantes con insoportables reflejos, portátiles innecesariamente delgados (el peso de un portátil importa mucho, que sean finos no aporta nada) que tienen teclados sin profundidad (el teclado del nuevo Macbook es realmente terrible), insistencia en los touchpad cuando los ratones y trackpoints son superiores, teclados a los que les faltan teclas vitales (reemplazando la útil “suprimir” por una peligrosísima tecla de apagado), etc. etc. Apple sacrifica constantemente la utilidad por la apariencia, y yo uso los ordenadores, no los contemplo.

El camino de la verdad y la virtud

Apple tiene claro cómo quiere que el usuario interactúe con sus dispositivos. Más allá de su deseo de controlar los contenidos y software que se pueden experimentar, también tiene una idea clara de los patrones de uso. Si bien debo decir que seguramente su visión sea de las más claras y coherentes del mercado y que su persecución de la simplicidad es acertada en la mayoría de los casos, uno no puede evitar pensar que para usar adecuadamente un producto Apple uno debe convertirse a su verdadera religión y su manera de hacer las cosas.

Creo que diferentes personas prefieren interactuar con los ordenadoras de diferentes maneras, y que cada uno puede encontrar la manera de trabajar más eficiente para él. Puede que lo haga para fastidiar, pero uso un entorno de escritorio muy peculiar (un “tiling window manager”), utilizo un móvil con teclado físico, prefiero las líneas de comandos y adoro los trackpoints. Apple reduce su diversidad al máximo y si quisiese adoptar sus productos, tendría que renunciar a muchas de estas cosas, ya que Apple nunca se dedicará a ellas. Y si bien su visión es completa y poderosa, a mi no me convence.

Prefiero el software libre

Como programador creo que el software libre es una alternativa muy atractiva al software propietario. Los intereses comerciales raramente se alinean con los de los usuarios (podría parecer que sí, y a veces coinciden, pero no), mientras que si el usuario puede participar activamente en el desarrollo del software, esta alineación es más probable. Mis experiencias en este sentido han sido muy positivas- he conseguido influir en el desarrollo de software que utilizo para obtener arreglos y mejoras que me han beneficiado directamente, algo que creo es muy complicado con el software propietario.

Adicionalmente, también me he beneficiado de poder estudiar y analizar código publicado por terceros, y creo que es una poderosa herramienta de aprendizaje para los nuevos desarrolladores de software.

Así pues, en la medida que sea posible utilizo software libre. Si bien Apple coopera con el software libre en ocasiones, lo hace a regañadientes y primando su interés comercial por encima de todo- y por supuesto, la mayoría del software que desarrolla no es libre.

Mi opinión sobre unos cuantos lenguajes de programación

(sobre el TIOBE de Agosto 2015, básicamente).

Java: junto con PHP, uno de los lenguajes más denostados del mundo. A diferencia de PHP, probablemente una de las mejores opciones disponibles para hacer software grande y complejo y no morir en el intento. Hay una escasez de lenguajes de tipado estático populares en el mundo y si no nos queremos ir a Microsoft, Java cuenta con un ecosistema brutal, da un rendimiento sobresaliente y se aprende fácilmente. Pero no es chachi.

C: el lenguaje que todos adoramos hasta que lo tenemos que usar en serio. Afortunadamente es el siglo XXI y la mayoría de nosotros no tenemos que recurrir a él. Eso sí, le debemos todo y aunque ya no domina el mundo, sus descendientes han heredado el trono.

C++: uno de esos lenguajes que siempre quiero aprender pero con el que nunca me pongo. El C++ auténtico se desliza entre la elegancia y potencia y el jeroglífico más ofuscado. Lamentablemente, no hay tiempo en esta vida para aprender todo lo que se necesita para programar bien en C++.

C#: el Java mejorado de Microsoft. Pero aunque lo estén abriendo, para disfrutarlo plenamente aún cuesta dinero. Sí, sé que cuesta poco para lo que vale, pero habiendo opciones gratuitas, ¿por qué molestarse?

Python: de los débilmente tipados, mi favorito. Un diseño bastante sencillo y ortogonal, alguna rareza por ahí pero en principio la mejor herramienta en muchas situaciones. Y si uno desarrolla web, el admin de Django es lo más disruptivo que existe en el terreno.

Objective-C: hasta Apple ya se ha dado cuenta que debe morir.

PHP: el horror. Cada característica de PHP cuenta con un problema fundamental, pero la gente insiste. Pero existe WordPress, Magento y tantos CMSs que es inevitable caer en sus redes de cuando en cuando.

VB.NET: C# con sintaxis de BASIC. No, gracias.

JavaScript: el lenguaje más accidentalmente popular del mundo. En realidad no es tan malo (salvo los callbacks en callbacks de callbacks) pero tampoco es bueno, y mucho menos como para que se intente que conquiste el mundo.

Perl: aguanta ahí, así como sus fieles. Excelente para ofuscar, confundir y compactar- aún no le he encontrado otra virtud.

Ruby: el sucesor de Ruby. Tiene su propio estilo de ofuscación- la maleabilidad es como la etimología indica, mala.

Haskell: posiblemente, el lenguaje más académico que es medianamente conocido y que goza de un buen ecosistema. Sus virtudes son incontestables, pero las profundidades teóricas en las que hay que sumergirse para entender sus conceptos más básicos es aterradora. La evaluación “perezosa” permite a su vez expresar algoritmos de maneras inimaginablemente elegantes pero hace que razonar sobre el tiempo y espacio de ejecución de un algoritmo sea doloroso.

Bash: posiblemente el mejor lenguaje de propósito específico que existe. Lo que se puede conseguir con unos comandos y unos pipes en un par de líneas y el mejor REPL del mundo hace que sea la herramienta más rápida para un número no despreciable de problemas. Eso sí, el lenguaje que peor escala del mundo.

SQL: el único lenguaje con un movimiento anti oficial. El modelo relacional es de momento el mejor sistema modelado que existe, las bases de datos relacionales de las plataformas más evolucionadas y eficientes del mundo y sin embargo… nadie quiere escribir SQL, todo el mundo prefiere escribir 10 veces más código en lenguajes menos apropiados para lo que necesitan.

Mi lenguaje de programación favorito

A todos nos gusta meternos en disquisiciones filosóficas inútiles, generalmente de la forma de “escoger el mejor X”. Para esta aburrida mañana de sábado, voy a hablar de mi lenguaje de programación preferido- un tema nada original.

Para empezar, ¿cuál es el criterio subjetivo para definir “favorito”?

Pues voy a escoger “productividad en su ámbito”; es decir, lo rápido que me permite resolver problemas en un área específica. Por supuesto este criterio está influenciado por los ámbitos en los que uno trabaja y requiere esta productividad.

Dentro de estos criterios, cada vez me es más difícil no responder “el bash”. Con el paso del tiempo he descubierto que para una amplia gama de situaciones, la mejor respuesta es indudablemente abrir un terminal y vomitar una sarta de bash y, muy frecuentemente, observar como funciona a la primera.

Podéis quejaros de que es un argumento un poco tramposo. Cuando uno habla de bash, nunca habla del bash en aislamiento- lo que hace realmente útil al bash es la infinidad de comandos que puede combinar- ya sea herramientas estándar como cut, grep, find, etc. como otras más evolucionadas como el curl u otras más esotéricas como el genial xmlstarlet- o incluso herramientas que son lenguajes de programación a escondidas, como el sed o el awk.

Pero argumentaré que el resto de lenguajes también son inútiles sin sus librerias- es sólo que el bash tiene una librería estándar descomunal que resulta ser cualquier cosa que se pueda ejecutar desde una línea de comandos.

El bash también cuenta con el concepto del pipe- presente en todos los shells pero sorprendemente ausente de la mayoría de los demás lenguajes de programación. El pipe es el pegamento perfecto para combinar cosas en el bash- la mayor parte de bashismos son cosas del tipo:

cat fichero | grep foo | cut -d , -f 3,5 | sed "s/^/foo:/" >resultado

; con esta sencilla primitiva combinamos sin fisuras cuatro comandos distintos- e infinitos más.

El conjunto del bash y el conjunto de herramientas estándar es tan armonioso que la mayor parte de manipulaciones de ficheros de texto, operaciones entre ficheros, etc. pueden resolverse con unos cuantos pipes y unos cuantos grep. Bash también nos permite definir funciones bastante ágilmente, cuenta con estructuras de flujo de control bastante majas, nos permite un primitivo control de errores y lo mejor de todo, permite reciclar nuestro código en scripts fácilmente reutilizables.

Si tuviese que escoger otro lenguaje favorito, por motivos similares escogería el SQL. SQL pelado, sí. Si puedes meter tu información en una base de datos relacional (y a menudo ya vive allí, porque las bases de datos son los mejores hogares para los datos), el SQL te permite con la misma brevedad que bash aplicar cualquier transformación a esos datos, extraer los que te interesan y sumarizarlos a gusto. En mi carrera profesional, pocas cosas se me han valorado más que la habilidad de extraer datos en minutos de una base de datos.

Lo más sorprendente de SQL es que, a diferencia del bash, es un lenguaje profundamente declarativo: raramente le decimos a SQL cómo tiene que hacer las cosas, sino que le decimos lo que queremos y él se preocupa de ejecutarlo de una manera eficiente (siendo capaz de realizar operaciones complejas sobre enormes conjuntos de datos en un periquete).

Detras del bash y SQL vendrían otros; Python por ser un lenguaje de propósito general claro y productivo (que además cuenta con Django- el framework para CRUD más productivo que he encontrado nunca)- Java por similares motivos, pero contando además con un rendimiento espectacular y características y herramientas esenciales para construir programas de gran tamaño, o Haskell, cuya elegancia matemática y su sorprendente modelo siempre me deja boquiabierto.

Pero antes de todos estos lenguajes populares siempre están esos dos- que muchos ni tendrían en cuenta como lenguajes de programación (pues son de ámbitos reducidos), pero que ejecutan funciones (específicas, eso sí- pero muy comunes) a la perfección.

NO quiero construir algo hermoso

El proyecto con más estrellitas ahora mismo en Github es el famoso Bootstrap (con más del doble de estrellas que su más inmediato perseguidor, jQuery), ese framework CSS beautiful y awesome que nos asegura que nuestra web tenga un aspecto único e individual como un extraordinario copo de nieve.

Viendo esto, me entra la sensación de que de alguna manera hemos abandonado el pasado sombrío donde los proyectos de software fracasan a raudales, que se pasan en costes y tiempos y que no tienen la mitad de la funcionalidad que requieren sus usuarios. Que encontrar la perfecta combinación de iconos, tipografías y widgets es la mayor preocupación ahora mismo- el backend es un problema resuelto y el frontend es donde tenemos que enfocar nuestros esfuerzos.

O eso, o somos seres simples e importar Bootstrap en nuestro proyecto (¡oh, no, perdón! Usar un framework que nos trae Bootstrap y media docena de opinionated awesomeness en una simple operación) y asegurarnos que todo tenga un borde redondeado es más entretenido y fácil que poner una funcionalidad de búsqueda en nuestra aplicación que sirva de algo. Implementar un wireframe de un autoproclamado experto en UI/UX nos dará mucha más usabilidad que asegurarnos que nuestro interfaz reporte errores al usuario de una manera clara y entendible.

Es una preocupante tendencia ahora mismo dedicar unas cantidades desproporcionadas de tiempo a la parte visual en detrimento de hacer cosas que funcionen, sean robustas y rápidas. Nuestra web cargará a toda pastilla en nuestra conexión de fibra con el flamante y bonito portátil recién estrenado- ¿a quién le importa el tipo que tira de 3G con un móvil de hace un par de años? Estamos lejos aún de que el desarrollo de hasta aplicaciones moderadamente simples sea rápido, eficiente y barato, pero hemos pulido hasta el infinito las técnicas para darle un bonito aspecto.

Sé perfectamente que soy un caso extremo- el CSS de este blog son 20 líneas (y son 20 líneas más de las que me gustarían), y hasta hace poco tenía un sistema en producción moderadamente popular cuyo backend tenía 0 CSS (de cuya austeridad, por cierto, en una empresa artística, no se quejaba nadie). Soy consciente de la necesidad de vender y que la apariencia es una de las claves para ello, pero me temo que en 10 o 20 años nos preguntaremos por qué narices le dedicamos tanto tiempo a inflar la burbuja y no dedicarnos a resolver los problemas de verdad.

¿Por qué el CRUD es importante?

No sé si se vislumbra completamente por aquí, pero llevo mucho, mucho tiempo prácticamente obsesionado con encontrar la solución para el CRUD. Ya desde mi primer curro, allá por el 2001-2002, donde usaba el jurásico ATG Dynamo- que pese a primitivo ya se enfrentaba al CRUD bastante frontalmente pude apreciar que en un significativo número de trabajos por los que te pueden pagar, el CRUD es vital.

No sólo porque sí, los programas informáticos suelen servir para eso, para manejar información. Create, Read, Update, Delete es lo que uno puede hacer con los datos, y lo hace muy a menudo, y si lo implementas rápido, ahorras dinero.

La verdadera importancia de esto reside en que si implementarlo es farragoso, eso te lleva por mal camino. Si necesitas un esquema de datos complicado e implementar el CRUD necesario para mantenerlo es costoso y tedioso, existe la poderosa tentación de simplificar el esquema. Denormalizamos, quitamos funcionalidad y en definitiva, guarreamos, porque hacerlo bien nos cuesta. Si hacer CRUD es costoso, tendemos a evitar la base de datos para guardar cosas y las metemos en el código- lo que podrían ser tablas de soporte editables por el usuario se convierten en configuración incrustada en el código que sólo puede cambiar el programador.

Por último, pero no menos importante, cuanto más cuesta implementar el CRUD, peor lo implementamos. Interfaces inflexibles, que no permiten ordenación y paginación, que distribuyen la información que nos interesa entre varias páginas que hacen que editarlo sea un suplicio, con desplegables inacabables y sin función de búsqueda… son males que se pueden evitar si nos vienen gratis.

Es por eso que es importante disponer de frameworks que simplifiquen el CRUD. Es frustrante que sólo Django tenga un CRUD potente y bien implementado, y que todos los demás estén uno o varios pasos por detrás. Es por este motivo que detrás de muchas aplicaciones de negocio lo que hay detrás en realidad es un framework CRUD, y que la calidad de éste es uno de los mayores factores que determinan la calidad del producto.

¿Qué necesita un buen CRUD? En mi opinión:

  • Poder crear fácilmente pantallas de listado de entidades, con filtrado, ordenación, paginado y búsqueda; que dé la opción de edición directa sobre el listado y que permita mostrar/editar información de entidades relacionadas
  • Poder crear fácilmente pantallas de edición de entidades, que tengan widgets adecuados para los tipos de datos que necesitamos, y que permita editar directamente entidades relacionadas, dando un interfaz adecuado a las claves foráneas y relaciones de todo tipo de cardinalidad y complejidad
  • Poder navegar ágilmente entre entidades relacionadas
  • Un sistema de permisos completo que permita restringir con granularidad por grupos de usuario hasta los niveles de columna y fila (i.e. qué registros puede editar cada usuario, y de cada uno, qué atributos)
  • Un sistema de workflows que permita manejar diversos estados para cada entidad, aplicar permisos sobre las transiciones y disparar eventos sobre los cambios de estado
  • Un sistema de auditoría de cambios que permita ver los históricos de modificaciones y si es factible, restaurar versiones anteriores
  • … y sobre todo, que todo lo anterior sea extensible, ya que siempre, siempre, necesitaremos más de lo que está previsto

Con esto, tenemos gran parte del camino recorrido en muchos proyectos. Sin embargo, el hecho de que aún Django, el primero de la clase, esté bastante cojo en algunos aspectos, me hace pensar que o bien esto no puede existir, o que los que lo han implementado no lo van a publicar o que me pierdo algo…

Código y sensibilidad

Cada persona, y los programadores no dejan de ser personas, desarrolla a lo largo del tiempo diferentes sensibilidades. A base de cometer errores y sufrir por ellos, nos hacemos especialmente perceptivos a los síntomas que nos causan dolor. Un programador de lenguajes de la familia de C es capaz de notar la ausencia de un ; para terminar una sentencia en una pantalla plagada de símbolos. Alguien que trabaje con SQL habitualmente desarrolla un sexto sentido para no lanzar un DELETE que no lleve WHERE (normalmente tras el tercer incidente grave). Y así, cada uno enumerará sus ejemplos favoritos en los que ha desarrollado una aptitud sobrehumana para evitar inacabables pérdidas de tiempo.

Es esto, por encima de otros factores, lo que nos hace expertos en algo. Saberse de pe a pa todos los comandos UNIX habituales no te convierte en experto- es saber que nunca debes ejecutar killall en Solaris, pese a lo útil que resulta este comando en Linux lo que demuestra que has vivido tus propios vietnames- y has aprendido de ellos.

Una observación curiosa sobre las sensibilidades es que estas no se adquieren así como así. Uno de los motivos por los que creo que las enseñanzas en materias de programación deben ser eminentemente prácticas y rehuyo la formación por exposición- sean cursos, sea un compañero que escriba un documento descriptivo sobre un área funcional/librería/arquitectura, ya que en general son una pérdida de tiempo. Saber cómo funciona algo es relativamente fácil- se puede acelerar pero raramente es el problema. El problema es que cuando no tenemos sensibilidad en lo que hacemos, todo nos cuesta un orden de magnitud más. Lo que necesitamos es ponernos manos a la obra y tener alguien con la sensibilidad que nos hace falta mirándonos por encima del hombro y arreándonos un sopapo cuando vayamos a hacer algo estúpido, o que nos desatasque cuando seamos incapaces de ver el bosque.

La sensibilidad no es más que un atajo heurístico que nos permite ser eficaces antes de haberlo aprendido y comprendido todo en el más absoluto detalle (algo frecuentemente necesario, pero no siempre), y que aún cuando somos expertos en algo, seguimos aplicando por su efectividad.

Seamos sensibles. Transmitamos nuestras sensibilidades. No perdamos el tiempo de entrada, lancémonos de cabeza- démonos golpes que desarrollen nuestra sensibilidad o aprovechemos los golpes que se han dado otros antes.

Integración, APIs y no me toques los bezos

Según Steve Yegge, un día Jeff Bezos, el capo de Amazon, envió un memorándum interno que venía a decir:

1) All teams will henceforth expose their data and functionality through service interfaces.

2) Teams must communicate with each other through these interfaces.

3) There will be no other form of interprocess communication allowed: no direct linking, no direct reads of another team’s data store, no shared-memory model, no back-doors whatsoever. The only communication allowed is via service interface calls over the network.

4) It doesn’t matter what technology they use. HTTP, Corba, Pubsub, custom protocols — doesn’t matter. Bezos doesn’t care.

5) All service interfaces, without exception, must be designed from the ground up to be externalizable. That is to say, the team must plan and design to be able to expose the interface to developers in the outside world. No exceptions.

6) Anyone who doesn’t do this will be fired.

Este puñetero memo, y toda la gente que lo ha leído y ha aplicado el silogismo falaz de “En Amazon son unos cracks, en Amazon siguen este credo ergo si yo sigo este credo seré un crack” son un maldito dolor de cabeza.

Conste que no es que considere que las APIs sean una plaga a exterminar, ni mucho menos, pero debería olernos mal las sentencias absolutistas y los razonamientos de talla única. No todos los entornos son iguales, ni se enfrentan a los mismos problemas- y por tanto lo que funciona para unos, no funciona para otros. Adicionalmente, es difícil razonar que la aplicación ciega de esta doctrina es lo que ha llevado a Amazon al éxito y estoy seguro que no en pocas ocasiones mejor les hubiera ido siendo un poco más críticos.

La realidad, maldita ella, siempre se interpone a los ideales. Implementar una API tiene un coste en tiempo que no siempre es amortizable por sus beneficios. Conste que el hecho que no se vaya a usar la citada API tampoco es un argumento tan fuerte en contra de estas- el hecho de añadir una API usable nos fuerza a hacer el código modular lo cual es un valioso beneficio en sí mismo que si puede valer la pena- aunque es perverso el argumento de ser modulares porque tenemos que implementar una API: seamos modulares porque está bien ser modular sin necesidad de dichosas APIs.

Pero más allá del coste de implementar la API, la imbezosidad esta muchas veces lleva a auténticos pantanazos donde dos departamentos de la misma empresa fuerzan que dos sistemas se comuniquen a través de una API. API a la que muchas veces no se le dedica el tiempo necesario y que no permite alcanzar todo lo que se necesita de una forma eficiente y efectiva, que fuerzan a implementar soluciones chapuceras que no benefician a nadie, cuando habría alternativas perfectamente efectivas.

Bezos prohibe explícitamente, por ejemplo, el acceso a base de datos entre aplicaciones- quizá la situación más común en el que la gente se arma del memorándum para tomar decisiones insensatas. “¡Os daremos una API maravillosa!”, “¡Permitirá que saquéis toda la información que necesitéis!”, “¡La extenderemos rápidamente con vuestras nuevas necesidades!”. Mentiras y más mentiras. Cuando yo te podría escribir un par de selects que me sacan todos los datos que necesito fácil y eficientemente, tú me darás una API castrada en la que tendré que hacer numerosas llamadas de API para obtener los mismos resultados y que se tendrá que extender cada vez que surja un nuevo requerimiento. Eso si lo extiendes, porque “oh, lo siento, ahora estamos liados con otra cosa” hará que no se pueda implementar tal requerimiento en no pocas ocasiones.

Sí, desde luego, esto no es algo universal tampoco- como tampoco lo es la doctrina de las API. Si son los mismas personas las que implementan la API y la consumen, o hay una suficiente coordinación de objetivos, esto no tiene por qué ser un problema. Sin embargo, muchas veces lo es. Y es precisamente para levantar vallas entre vecinos uno de los motivos con los que se esgrime el credo- consciente o inconscientemente.

¿La solución? No sigas ninguna regla ciegamente. Como siempre.

Recopilatorios de grandes éxitos

Indudablemente, una de las asignaturas coco de cuando yo estudiaba informática era Compiladores- compis, para los amigos. No porque hubiese una tonelada de apuntes que memorizar, ni porque los profesores fuesen particularmente estrictos sino porque, simplemente, era tremendamente complicada.

Implementar un lenguaje de programación es seguramente una de las labores más complicadas que hay en el campo de la programación pura.  Tanto intérpretes como compiladores son fieras complejas que necesitan el uso de técnicas bastante sofisticadas para poder ser programadas efectivamente.

Tanto intérpretes como compiladores tienen que leer los programas escritos en su lenguaje- algo no trivial que en general se resuelve usando herramientas descendientes de los venerables lex y yacc; que en conjunto convierten una descripción de un lenguaje (un listado y definición de los átomos del lenguaje; palabras clave, separadores, sintaxis de literales, etc.; y una descripción de cómo se combinan para formar las estructuras del lenguaje) y que típicamente generan código en el que incrustamos nuestro compilador e intérprete.

Estas herramientas, básicas para el desarrollo, no son tampoco precisamente simples. Definir gramáticas de lenguajes es algo que se complica rápidamente- si no estamos hablando de lenguajes simples, no es sencillo encontrar una sintaxis y gramática no ambigua y, curiosamente, que no sea exponencialmente compleja de procesar. Lenguajes como C++ y Perl son notorios por sus particularmente enrevesadas gramáticas- en ambos casos son de tal complejidad que ellas mismas son, en cierta manera, lenguajes de programación.

Una vez podemos tenemos un mecanismo que nos permite pasar del código fuente de nuestro lenguaje a algo tratable por un programa (que es aún más complicado de lo que hemos descrito, ya que las herramientas comentadas, a parte de su complejidad teórica, nos plantean dificultades prácticas porque suelen ser, en general, bastante inaccesibles y poco amistosas), nos queda lo peor.

Los intérpretes deben, como su nombre indica, interpretar el código ya dispuesto en una estructura tratable (aunque probablemente compleja) y ejecutarlo. Para ello, debemos coger la representación del código e ir ejecutándolo paso a paso igual que haríamos si quisiesemos verificar la ejecución del código manualmente. Esto tampoco es sencillo, ya que características del lenguaje tales como los distintos alcances de los identificadores, los sistemas de objetos, la comprobación de tipos, etc. son en general problemas de por sí complejos.

Pero quizás el hueso más duro de roer es la compilación. En un problema algo relacionado con la traducción de lenguaje natural, un compilador debe coger el código una vez procesado y presentado de una manera estructurada y convertirlo en código de otro lenguaje. Esto, tal como es la traducción de lenguaje natural, es harto difícil.

En general, los compiladores suelen ser de lenguajes de mayor nivel a menor nivel; solía ser que los lenguajes se compilaban directamente al lenguaje ensamblador propio de las CPU- lenguaje ensamblador que es mucho más simple, poco expresivo y limitado que la mayoría de lenguajes de alto nivel, y por tanto esta traducción es compleja- en cierta manera debemos coger Dublineses y explicarlo de manera que un niño de cuatro años pueda entenderlo perfectamente.

En estos casos, la cosa se suele complicar mucho más porque una implementación naíf de un compilador a ensamblador suele redundar en programas que al ejecutarse son espectacularmente ineficientes. Conseguir ejecutables eficientes es un problema completo en sí mismo sobre el que se han escrito toneladas de libros.

Sin embargo, recientemente son más habituales los compiladores que compilan lenguajes a cosas que no son ensamblador- por diversos motivos entre los que destaca que un compilador a ensamblador sólo es útil para una familia de CPUs, y pese a que la familia x86 de Intel y los ARM copan la mayor parte del mercado, sigue habiendo muchos otros procesadores en uso hoy en día. Por otra parte, plataformas como la máquina virtual Java, LLVM o incluso Javascript también son populares como destinos de los compiladores- en el caso de Java o LLVM por ser más simples para la generación de código sin sacrificar eficiencia, y en el caso de Javascript, por ser un destino particularmente útil ya que nos permite ejecutar el código compilado en un navegador.

Tanto la JVM como la LLVM han sido diseñadas especialmente para este propósito, con lo que tienden a simplificarnos el proceso de compilación. En el caso de Javascript, pese a estar pensado con otros propósitos, proyectos como GWT o Emscripten han hecho grandes esfuerzos para hacer funcionar compiladores sobre Javascript. Mozilla incluso ha lanzado la iniciativa asm.js para definir un subconjunto de Javascript que sea práctico como plataforma a la que compilar de una manera eficiente.

El proceso no se queda aquí, ya que una vez tenemos un lenguaje funcional con intérprete o compilador, siempre hay un interés en acelerarlo- tanto el proceso de compilación como la ejecución de los programas. Una vez más, se trata de un área complicada y sutilezas- se han llegado a técnicas extremadamente sofisticadas que incluso “aprenden” de la ejecución del programa y modifican su funci0namiento para adaptarse y mejorar “en vivo”.

Como decíamos, una de las áreas de la informática pura más complejas y duras. Si bien en inteligencia artificial, bases de datos, proceso de imágen, etc. existen problemas duros, no suelen ser tan duros en cuanto a programación, sino a las matemáticas y otras áreas no estrictamente informáticas que tocan. También en programación empresarial existen sistemas extraordinariamente complejos, pero en este caso suelen serlo por el tamaño y la cantidad de entidades y conceptos interactuando entre sí del negocio. Pero son los compiladores probablemente una de las hazañas de programación más sofisticadas que hay y, así mismo, primordial para la programación en sí.

Esquemas de datos explícitos e implícitos

Hablando de bases de datos relacionales, es común referirse al esquema de datos como la definición de las tablas, vistas, funciones, etc. que conforman la base de datos*. El esquema es sumamente importante, por supuesto, nos define qué datos admitimos y nos condiciona todo código que accede a la base de datos.

Este es un esquema explícito; está ahí, podemos enumerar los objetos de los que se compone y conocer milimétricamente su estructura; podemos saber qué tablas hay y qué columnas tienen, etc.

Ahora bien, supongamos que cogemos una aplicación que usa una base de datos y de repente, ocultamos el esquema. Asumamos por un momento que no podemos saber qué tablas hay, ni qué columnas, ni nada. Aún haciendo este gran cambio, nuestra aplicación seguirá funcionando**. Aún más, si desconectamos todas las restricciones de integridad y admitimos que se inserten valores en columnas que no existen (e incluso los almacenamos)… nuestra aplicación muy probablemente seguirá funcionando correctamente.

La primera observación interesante que podemos hacer es que aquello a lo que llamábamos el esquema de datos puede que no esté ahí, pero los datos que tenemos almacenados seguirán siguiendo el anterior esquema de datos. La programación seguirá condicionada por ese mismo esquema; seguiremos insertando en las tablas y columnas que definía el esquema.

El esquema de datos original que habíamos declarado explícitamente ya no existe, pero el esquema de datos implícito sigue ahí. Los datos continuan cumpliéndolo y el programa sigue regido por él. Existe, por tanto, un esquema implícito que podríamos deducir con bastante certeza observando los datos almacenados y el código que los manipula.

Siguiendo con este experimento, podríamos preguntarnos… ¿no hemos perdido nada, verdad?

Aparentemente, no. En cuanto a funcionamiento, quizá ocultemos y amplifiquemos algún defecto que ya existía (la base de datos aceptará datos inválidos cuando antes los rechazaba, y muy probablemente, no nos daremos cuenta), pero realmente no habremos perdido mucho.

Pero cuando nos llegue el momento de modificar o ampliar el código, sí tendremos un problema: al no poder consultar el esquema, se dificultará mucho nuestra labor. El esquema explícito era eso: explícito, claro, fácil. El esquema implícito sigue ahí, pero está oculto. Lo duro es que antes bastaba con ajustarnos al esquema explícito de los datos, que estaba delante de nuestros ojos, pero ahora seguimos teniéndo que seguir un esquema de datos implícito, mucho más críptico. Tendremos que mirar los datos almacenados o el código para saber cómo se llamaba cada tabla y cada columna, y esta información muy probablemente no esté centralizada.

Por supuesto, hay un caso en el que sí seguirá disponible. Si usamos un algo como un ORM, podremos contar con otro esquema explícito de datos; la definición del ORM -e incluso en algunos casos podremos reconstruir perfectamente el esquema a partir de la definición del ORM- claramente son conceptos si no equivalentes siempre, muy cercanos***. Si este esquema es suficientemente bueno, podría suplir perfectamente al esquema explícito de las bases de datos (e incluso mejorarlo: podría permitirnos expresar un esquema de datos más restringido).

Podríamos decir que el esquema de datos explícito de la base de datos no es estrictamente necesario, pero ciertamente, un esquema de datos explícito es una herramienta muy útil, quizás no tanto para el funcionamiento de las aplicaciones como para su codificación y mantenimiento.

Adicionalmente, es bien cierto que puedan existir datos que no se ajusten al modelo relacional -cuya estructura o restricciones sean sumamente especiales (normalmente por ser extremadamente laxos), pero aunque esto fuera cierto, seguirían teniendo un esquema implícito (o incluso explícito, si la herramienta que usáramos en sustitución de la base de datos lo permitiera o requiriera, o si nosotros  decidiéramos escribirlo) y, igual que en el caso relacional, no disponer de un esquema explícito nos entorpecería las labores de mantenimiento y extensión del código.

Así pues, aunque es posible que un esquema relacional no sea lo más adecuado para nuestros datos, es falaz concluir que la ausencia de un esquema explícito es una ventaja -el esquema implícito sigue existiendo y nos debemos ajustar a él- y es más fácil ajustarse a algo explícito y claro que a algo implícito y oculto.

* en algunos gestores de bases de datos, el término se confunde un poco porque se pueden separar los objetos de la base de datos en “esquemas” para gestionarlos mejor

** las bases de datos permiten a las aplicaciones consultar el esquema [explícito]; hay aplicaciones que utilizan esta funcionalidad (para permitir su personlización mediante la creación de nuevas tablas, etc.); en este poco frecuente caso, dejarían de funcionar, claro

*** lo que nos debería llevar a pensar que uno de los dos es redundante e innecesario

corregido por el de siempre

Amigo lector

Se han escrito ríos de tinta sobre la muerte de Google Reader. Una de las mayores víctimas silenciosas, son los usuarios de Blackberry. Hasta el momento, yo usaba la web de iPhone; un vestigio raro ya que hay “app”, pero que era la mar de usable en mi móvil.

De entre todos los reemplazos de Google Reader que se barajan, aún no he encontrado ninguno que me solucione el uso móvil en la Blackberry- no parece haber nadie que tenga una web móvil usable [la de Netvibes ya os digo yo que no se puede usar] y de momento no se huele ninguna aplicación (y por mucho que esto me estigmatice, preferiría no pagar si es necesario [aunque en el caso del lector de feeds, podría hacer una excepción]).

Explorando alternativas, me he puesto un poco en plan hágaselo usted mismo. Feedly dice que implementará Normandy, un clon del backend de Google Reader y de momento apostaré por esta reimplementación. De hecho, Google Reader no tiene API “oficial”, pero mucha gente la ha destripado y documentado, y de hecho goza de cierta popularidad. Así pues, me he puesto a buscar librerías que implementen la API.

Dado que uno trabaja mayoritariamente Python/Django y Java, esto me ha limitado un poco la búsqueda. Para Java, el mejor candidato parece greader-unofficial, pero parece estar un poco muerto con tan solo 47 commits en su repositorio, el último de octubre de 2011. En cambio, naturalmente parece que para Python existe libgreader, con commits de anteayer y con mucha mejor pinta.

Así pues me he puesto manos a la obra y he iniciado un proyecto Django publicado en Github, GROLM (Google Reader on Lightweight Mobile). Es un proyecto Django 1.5 estándar sobre una base de datos PostgreSQL.

libgreader proporciona cuatro alternativas a la hora de manejar la autenticación contra Google Reader:

  • proporcionarle el usuario/contraseña de la cuenta Google y que él mismo autentique
  • OAuth
  • OAuth2
  • una variante de OAuth para Google App Engine

; obviamente la menos usable y/o poco segura es la de la contraseña, y tampoco quiero desarrollar sobre App Engine, así que me he decantado por OAuth2 (al ser más moderna obviamente que OAuth). El mecanismo de autenticación de OAuth2 de libgreader parece completo, pero deseaba integrarlo con el sistema de autenticación de Django para ahorrarme faena.

Para ello, he localizado django-social-auth, que integra en el sistema de autenticación de Django diversos mecanismos de login social entre los que se incluye OAuth2. La configuración de django-social-auth con OAuth2 es relativamente sencilla, simplemente tenemos que añadir el siguiente fragmento a nuestro settings.py:

INSTALLED_APPS = (
  # ...
  'social_auth',
)
AUTHENTICATION_BACKENDS = (
  'social_auth.backends.google.GoogleOAuth2Backend',
)
GOOGLE_OAUTH_EXTRA_SCOPE = [
  'https://www.googleapis.com/auth/userinfo.email',
  'https://www.googleapis.com/auth/userinfo.profile',
  'https://www.google.com/reader/api/',
 ]
LOGIN_URL = '/login/google-oauth2'
LOGIN_REDIRECT_URL = '/logged-in/'
LOGIN_ERROR_URL = '/login-error/'
GOOGLE_OAUTH2_CLIENT_ID = 'xxxx'
GOOGLE_OAUTH2_CLIENT_SECRET = 'xxxx'

, donde los dos últimos parámetros los podemos obtener de la Google API Console (donde también deberemos añadir las URLs de nuestra aplicación para validar el proceso de login)

Con esto, con anotar una vista como @login_required, al visitarla se nos redirigirá a la página de autenticación de las cuentas Google donde haremos login y se nos redirigirá de nuevo a la vista ya autenticados (se creará un usuario de Django correspondiente al usuario de Google y se vinculará a la sesión del usuario).

Finalmente, para “propagar” esta autenticación a libgreader, el mecanismo que he encontrado es extraer el token de acceso que recoge django-social-auth y almacena en una base de datos y pasárselo a libgreader, algo que he encapsulado en un módulo, pero que en realidad es bastante sencillo:

usa = UserSocialAuth.objects.get(user=user)
 auth = OAuth2Method(settings.GOOGLE_OAUTH2_CLIENT_ID, settings.GOOGLE_OAUTH2_CLIENT_SECRET)
 auth.authFromAccessToken(usa.extra_data['access_token'])
reader = GoogleReader(auth)

, donde user es el usuario (que desde una vista podemos obtener por ejemplo haciendo request.user). Una vez tenemos esto, ya obtenemos el objeto GoogleReader que constituye el punto de acceso a la API.

El resto de código implementado por el momento tan sólo obtiene la reading-list (los elementos sin leer) y muestra sus 20 primeras entradas.