26 marzo, 2009

js.php, una clase PHP cual objeto JavaScript

Andrea Giammarchi vuelve a demostrar un profundo conocimiento de los lenguajes de programación, necesario para llegar a proponer una forma de trabajar con objetos en PHP que copia el sistema de prototipos de JavaScript y permite realizar cosas como las siguientes:

// podemos asignar prototipos en cualquier sitio ...
JSObject::$prototype->getName = function($self){
    return  $self->name;
};

$o = JSObject();

// ... incluso después de la creación de instancias de JSObject 
JSObject::$prototype->writeAge = function($self){
    echo $self->getName().' tiene '.$self->age.' años.';
};

// asignar propiedades
$o->name    = 'Andrea';
$o->age     = 30;

// probar algún método JS
echo    $o->toJSONString();
// {"name":"Andrea","age":30}

echo    $o;
// [object JSObject]

echo    $o->toSource();
// O:8:"JSObject":2:{s:4:"name";s:6:"Andrea";s:3:"age";i:30;}

// o probar métodos en tiempo de ejecución
$o->writeAge();
// Andrea tiene 30 años

// podemos asignar un método personalizado en tiempo de ejecución
$o->getSurname = function($self){
    return  'Giammarchi';
};

// los métodos personalizados tienen prioridad sobre el prototipo
$o->getName = function($self){
    return  $self->name.' '.$self->getSurname();
};

$o->writeAge();
// Andrea Giammarchi tiene 30 años.


// no es hermoso? y aún falta más para la compatibilidad hacia atrás (backward):

// prototipado via resolución de nombres de función
JSObject::$prototype->getName = 'JSObject_getName';

// la función con un prefijo evita conflictos
function JSObject_getName($self){
    return  $self->name;
}

// un simple caso de prueba
$o = JSObject();
$o->name    = 'Andrea';

echo    $o->getName();
// Andrea

// Y obviamente podemos hacer lo mismo con create_function:

// prototype via lambda
JSObject::$prototype->getName = create_function('$self','
    return  $self->name;
');

La implementación utiliza la extensión SPL disponible en el núcleo de PHP desde la versión 5.1:

/** js.php basic JSObject implementation
 * @author  Andrea Giammarchi
 * @blog    WebReflection.blogspot.com
 * @license Mit Style License
 */
class JSObject implements ArrayAccess {

    // public static prototype
    static  public  $prototype;

    // ArrayAccess Interface
    public  function    offsetExists($index){return  isSet($this->$index);}
    public  function    offsetGet($index){return  $this->$index;}
    public  function    offsetSet($index, $value){$this->$index = $value;}
    public  function    offsetUnset($index){unset($this->$index);}

    // Invoked lambdas via $this or self::$prototype
    public  function    __call($index, array $arguments){

        // inject $this as first argument
        array_unshift($arguments, $this);

        // invoke the callback
        return  call_user_func_array(
        
            // if it has been assigned to the object itself
            isset($this->$index) ?

                // it has priority
                $this->$index :
                
                // otherwise invoke from self::$prototype
                self::$prototype->$index
            ,
            $arguments
        );
    }

    // JS like behavior, ready for extends
    public  function    __toString(){
        return  '[object '.get_class($this).']';
        // e.g. [object JSObject]
    }

    // Relevant Global Object implementations
    public  function    getPrototypeOf(){
        return  get_class($this);
    }
    public  function    hasOwnProperty($index){
        return  isset($this->$index);
    }
    public  function    isPrototypeOf(/* string */ $class){
        return  $this instanceof $class;
    }
    public  function    toSource(){
        // we could implement __sleep and __wakeup
        // to avoid closure serialization (not possible yet)
        return  serialize($this);
    }
    public  function    toString(){
        return  $this->__toString();
    }


    // not standard method, anyway useful
    public  function    toJSONString(){
        // we could parse properties in a foreach
        // to filter meaningless closures, if any
        return  json_encode($this);
    }
}

// the global prototype is a JSObject too
JSObject::$prototype = new JSObject;

// JSObject factory: e.g. $myobj = JSObject(); // rather than new JSObject;
function JSObject(){
    return  new JSObject($object);
}

Como apunta Andrea, este puede ser el punto de partida para la creación de frameworks de PHP utilizando este estilo de programación. También menciona que unas extensiones estables de PECL que permitiesen una sobrecarga de operadores (de la que yo no soy muy amigo, por cierto) le permitiría crear una versión de PHP estilo JavaScript. Y aprovechar eso para conseguir que PHP se comporte como JavaScript en el lado del servidor al estilo Jaxer, pero utilizando PHP que está mucho más difundido.

WebReflection: [js.php] A JavaScript Object Like PHP Class.

Sly, el nuevo motor de selección CSS

Hace unos meses se levantó cierta polémica con la propuesta de John Resig de que todas las frameworks adoptasen su proyecto Sizzle para aunar esfuerzos y beneficiarse todos de las mejoras aportadas por la comunidad de desarrolladores. MooTools decidió seguir su propio camino explicando sus argumentos y John Resig les dijo que tendrían que resignarse a estar siempre intentando alcanzarles. Pues parece ser que hoy es el día que va a ser Sizzle (y por tanto Resig) quien va a tener que esforzarse por alcanzar este nuevo motor.

Sly es el nuevo motor de selección de MooTools, los gráficos lo dicen todo:



Características:

  • Algoritmo de búsqueda realizado enteramente con Javascript para consultas rápidas y precisas
  • Optimizaciones extra para selectores usados frecuentemente y para las últimas características de los navegadores
  • Funciona uniformemente con documentos DOM, fragmentos y documentos XML
  • Métodos de utilidad para encontrar y filtrar elementos
  • Parseador independiente de selectores para producir representaciones de objetos JavaScript
  • Personaliza pseudo-clases, operadores de atributos y combinadores
  • Sólo pesa 3kB (minimizado y comprimido, y 8kB sin comprimir)
  • Sin dependencias de librerías JS de terceros, pero los desarrolladores pueden sobreponer métodos internos (como getAttribute) para una integración transparente.
  • El código sigue la filosofía MooTools, respetando los estándares estrictos, lanzando warnings y usando nombres de variables con significado.

24 marzo, 2009

Cinco especificaciones nuevas con CSS y SVG

Están basadas en propuestas de Apple (y su trabajo sobre WebKit para Safari):

  1. SVG Transforms 1.0, Part 2: Language, Lenguaje de transformaciones con SVG
  2. CSS 2D Transforms Module Level 3, Transformaciones 2D con CSS
  3. CSS 3D Transforms Module Level 3, Transformaciones 3D con CSS
  4. CSS Animations Module Level 3, Animaciones con CSS permitiendo la modificación automática de valores de propiedades a lo largo del tiempo
  5. CSS Transitions Module Level 3, Transiciones con CSS que permiten cambios en las propiedades de valores CSS y que ocurran suavemente sobre una duración especificada.

Personalmente sigo echando en falta una extensión de CSS (u otro estándar) que permita definir layouts de una forma mucho más programática que la actual. El sistema actual de floats sólo está consiguiendo hacer perder una cantidad inmensa de tiempo a los desarrolladores que además se ven obligados a modificar el orden de los elementos y pegarse con los distintos navegadores para conseguir el resultado deseado. Por eso cada vez más voces se alzan pidiendo volver al sistema de tablas.

Via Ajaxian.

22 marzo, 2009

Cómo FriendFeed usa MySQL para almacenar datos sin esquema

Traducido de How FriendFeed uses MySQL to store schema-less data del blog de Bret Taylor

Fondo


Utilizamos MySQL para almacenar todos los datos en FriendFeed. Nuestra base de datos ha crecido mucho conforme lo ha hecho nuestra base de usuarios. Ahora guardamos más de 250 millones de entradas y un montón de otros datos, desde comentarios y gustos hasta listas de amistades.

Conforme ha crecido nuestra base de datos, hemos intentado tratar iterativamente con los problemas de escalado que comporta un crecimiento rápido. Hicimos lo típico, como utilizar esclavos de lectura y memcache para incrementar nuestro rendimiento en lectura o fragmentar (sharding) nuestra base de datos para mejorar el rendimiento en escritura. En cualquier caso, conforme crecíamos, escalar nuestras características para acomodar más tráfico resulto ser mucho menos problemático que añadir nuevas características.

En particular, hacer cambios en el esquema o añadir índices a una base de datos con más de 10 - 20 millones de registros bloquea la base de datos completamente durante horas. Eliminar viejos índices lleva el mismo tiempo, pero no eliminarla daña el rendimiento ya que la base de datos continuará leyendo y escribiendo esos bloques sin usar en cada INSERT, desalojando otros bloques importantes fuera de la memoria. Hay procedimientos operacionales complejos que se pueden hacer para sortear estos problemas (como crear un nuevo índice en un esclavo y entonces intercambiar maestro y esclavo), pero esos procedimientos son tan propensos a errores y tan pesados, que implícitamente nos disuadieron para añadir nuevas características que requiriesen cambios en los esquemas o índices. Ya que nuestras bases de datos están todas fuertemente fragmentadas, las características relacionales de MySQL como JOIN nunca nos han resultado útiles, por lo que decidimos mirar fuera del reino de las bases de datos relacionales (RDBMS).

Hay muchos proyectos diseñados para abordar el problema almacenando los datos con esquemas flexibles y construir nuevos índices al vuelo (p.e. CouchDB). En cualquier caso, ninguno de ellos parece lo suficientemente utilizado por sitios grandes como para inspirar confianza. En los tests que leímos y que llevamos a cabo nosotros mismos, ninguno de los proyectos eran estables o lo suficientemente probados en casos reales para nuestras necesidades (mira este algo desfasado artículo sobre CouchDB, como ejemplo). MySQL funciona. No corrompe los datos. La replicación funciona. Ya comprendimos sus limitaciones. Nos gusta MySQL para almacenar, pero no los patrones de uso relacionales (RDBMS).

Tras algo de deliberación, decidimos implementar un sistema de almacenamiento "sin esquemas" sobre MySQL en lugar de usar un sistema de almacenamiento totalmente nuevo. Este artículo intenta describir en líneas generales los detalles del sistema. Somos curiosos sobre cómo otros grandes sitios han abordado estos problemas, y pensamos que algo del trabajo de diseño que hemos realizado podría ser útil a otros desarrolladores.

Visión General


Nuesto almacén de datos guarda bolsas de propiedades sin esquema (p.e. como objetos JSON o diccionarios de Python). La única propiedad requerida para las entidades almacenadas es un id, un UUID de 16 bytes. El resto de la entidad es opaca en cuanto a lo que incumbe al almacén de datos. Podemos cambiar el "esquema" almacenando simplemente nuevas propiedades.

Indizamos datos en estas entidades almacenando los índices en tablas separadas de MySQL. Si queremos indizar tres propiedades en cada entidad, tendremos tres tablas MySQL - una por cada índice. Si queremos dejar de usar un índice, dejamos de escribir en esa tabla desde nuestro código y, opcionalmente, eliminamos la tabla de MySQL. Si queremos un nuevo índice, creamos una nueva tabla MySQL para ese índice y ejecutamos un proceso para poblar asíncronamente ese índice sin perturbar nuestro servicio activo.

Como resultado, terminamos teniendo más tablas que antes, pero añadir y eliminar índices resulta fácil. Tenemos un proceso fuertemente optimizado que puebla nuevos índices (al que llamamos "El Limpiador") de forma que rellena nuevos índices rápidamente sin perturbar el funcionamiento del sitio. Podemos almacenar nuevas propiedades e indizarlas en un mismo día en lugar de tener que hacerlo a lo largo de una semana, y no necesitamos intercambiar maestros y esclavos de MySQL ni hacer otras operaciones delicadas.

Detalles


En MySQL nuestras entidades se almacenan en una tabla similar a la siguiente:

CREATE TABLE entities (
    added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    id BINARY(16) NOT NULL,
    updated TIMESTAMP NOT NULL,
    body MEDIUMBLOB,
    UNIQUE KEY (id),
    KEY (updated)
) ENGINE=InnoDB;

La columna added_id está presente porque innoDB almacena los registros de datos físicamente usando el orden de la clave primaria. La clave primaria AUTO_INCREMENT fuerza a que las nuevas entidades se escriban secuencialmente en disco después de las entidades antiguas, lo que ayuda a la lectura y escritura locales (las nuevas entidades tienden a ser leídas más frecuentemente que las viejas entidades ya que las páginas de FriendFeed siguen un orden cronológico inverso). Los cuerpos de las entidades se guardan usando compresión zlib sobre diccionarios Python.

Los índices son almacenados en tablas separadas. Para crear un nuevo índice, creamos una nueva tabla que almacene los atributos que queremos indizar en todos los fragmentos de nuestra base de datos. Por ejemplo, una entidad típica en FriendFeed podría parecerse a esto:

{
    "id": "71f0c4d2291844cca2df6f486e96e37c",
    "user_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
    "feed_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
    "title": "We just launched a new backend system for FriendFeed!",
    "link": "http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
    "published": 1235697046,
    "updated": 1235697046,
}

Queremos indizar el atributo user_id de estas entidades de forma que podamos generar una página con todas las entidades que un usuario dado ha publicado. Nuestra tabla de índice podría parecerse a esto:

CREATE TABLE index_user_id (
    user_id BINARY(16) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (user_id, entity_id)
) ENGINE=InnoDB;

Nuestro almacén de datos mantiene automáticamente los índices por nuestra parte, por lo que para iniciar una instancia de nuestro almacén de datos que guarde las entidades como la estructura superior con los índices dados, se escribiría lo siguiente (en Python):

user_id_index = friendfeed.datastore.Index(
    table="index_user_id", properties=["user_id"], shard_on="user_id")
datastore = friendfeed.datastore.DataStore(
    mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"],
    indexes=[user_id_index])

new_entity = {
    "id": binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"),
    "user_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"),
    "feed_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"),
    "title": u"We just launched a new backend system for FriendFeed!",
    "link": u"http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
    "published": 1235697046,
    "updated": 1235697046,
}
datastore.put(new_entity)
entity = datastore.get(binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"))
entity = user_id_index.get_all(datastore, user_id=binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"))

La clase Index de arriba busca la propiedad user_id en todas las entidades y automáticamente mantiene el índice en la tabla index_user_id. Ya que nuestra base de datos está fragmentada, el argumento shard_on se utiliza para determinar qué fragmento contiene al índice (en este caso sería entity["user_id"] % num_shards).

Puedes realizar una consulta sobre índice usando una instancia de índice (ver user_id_index.get_all arriba). El código del almacén de datos hace el "join" entre la tabla index_user_id y la tabla entities en Python, consultando primero las tablas index_user_id en todos los fragmentos para obtener una lista de los IDs de entidades y entonces extrayendo los IDs de esas entidades de la tabla de entities.

Para añadir un nuevo índice, p.e. en la propiedad link, crearíamos la siguiente tabla:

CREATE TABLE index_link (
    link VARCHAR(735) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (link, entity_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Cambiaríamos nuestro código de inicialización para incluir este nuevo índice:

user_id_index = friendfeed.datastore.Index(
    table="index_user_id", properties=["user_id"], shard_on="user_id")
link_index = friendfeed.datastore.Index(
    table="index_link", properties=["link"], shard_on="link")
datastore = friendfeed.datastore.DataStore(
    mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"],
    indexes=[user_id_index, link_index])

Y podríamos popular el índice asíncronamente (incluso mientras se sirve tráfico en vivo) con:

/rundatastorecleaner.py --index=index_link

Consistencia y atomicidad


Ya que nuestra base de datos está fragmentada, y los índices para cualquier entidad pueden ser almacenados en diferentes fragmentos de las mismas entidades, la consistencia es un asunto a resolver. ¿Qué pasa si el proceso termina inesperadamente antes de haber escrito a todas las tablas índice?

Construir un protocolo de transacciones resultaba atractivo para los ingenieros más ambiciosos de FriendFeed, pero deseábamos mantener el sistema lo más simple posible. Decidimos relajar restricciones de forma que:

  • La bolsa de propiedades almacenada en la tabla entities es canónica
  • Los índices podrían no reflejar el estado actual.

Consecuentemente, escribimos una nueva entidad en la base de datos siguiendo los siguientes pasos:

  1. Escribe la entidad en la tabla entities usando las propiedades ACID de InnoDB
  2. Escribir índices para todas las tablas índice en todos los fragmentos

Cuando leemos de las tablas de índice, ya sabemos que podrían no ser precisas (p.e. podrían reflejar los antiguos valores si la escritura no ha terminado el paso 2). Para asegurar que no devolvemos entidades inválidas basadas en las restricciones anteriores, usamos tablas índice para determinar qué entidades leer, pero reaplicamos los filtros de la consulta por nuestra cuenta en lugar de confiar en la integridad de los índices:

  1. Lee la entity_id de todas las tablas índice basadas en la consulta
  2. Lee las entidades de la tabla entities a partir de los IDs obtenidos de las entidades
  3. Filtra (en Python) todas las entidades que no coincidan con las condiciones de la consulta basándose en los valores reales de las propiedades

Para asegurar que los índices no se pierden a perpetuidad y que las inconsistencias son corregidas eventualmente, el proceso "limpiador" que mencioné antes se ejecuta contínuamente sobre la tabla de entidades, escribiendo índices perdidos y limpiando los índices viejos e inválidos. Primero limpia entidades actualizadas recientemente, de forma que las inconsistencias en los índices se corrigen bastante rápidamente en la práctica (en un par de segundos).

Rendimiento


Hemos optimizado un poco nuestros índices primarios en este nuevo sistema, y estamos bastante complacidos con los resultados. Aquí hay un gráfico de la latencia de visión de páginas de FriendFeed durante el mes pasado (lanzamos el nuevo sistema hace un par de días, lo que explica la dramática caída):



En particular, la latencia de nuestro sistema es ahora remarcablemente estable, incluso durante las horas punta de mitad del día. Aquí hay un gráfico de la latencia de visión de páginas de FriendFeed de las últimas 24 horas:



Compárese con la de hace una semana:



El sistema ha sido realmente fácil de implementar. Ya hemos cambiado los índices un par de veces desde que desplegamos el sistema, y hemos empezado a convertir algunas de nuestras mayores tablas MySQL para utilizar este nuevo esquema de forma que podamos cambiar su estructura con más libertad en el futuro.

20 marzo, 2009

Envío de múltiples archivos con indicador de progreso y sin Flash

El siempre interesante Andrea Giammarchi anuncia un nuevo método inventado por él para subir múltiples archivos al servidor sin usar Flash (ni ningún otro componente), y que muestra el progreso de la transferencia de los archivos mediante AJaX/Javascript. Además es compatible con todos los navegadores.

Demo:



noSWFUpload - Zero Plug-In Multiple Upload

Búsquedas rápidas en Flickr

Via Simon Willison: Flickr ahora realiza una carga perezosa de la lista entera de contactos en memoria para realizar el autocompletado al introducir parte del nombre de un contacto. Se han realizado pruebas extensivas hasta encontrar que delimitar las cadenas con carácteres de control (uno para separar contactos y otro para separar los campos de un mismo contacto) era la opción más rápida para enviar miles de contactos eficientemente. La función Javascript split realiza el trabajo:

Building Fast Client-side Searches

Addendum: También dan detalles en Ajaxian.

IE8 disponible, pero se queda corto...

Cuando escribas sobre el nuevo IE8, no olvides una línea que indique que no ha sido resuelto el bug del writing-mode (cuando una tabla contenga una mezcla de writing-modes, las celdas se expandirán más allá de los anchos y altos necesarios). Se trata de la única característica de CSS3 implementada por esta versión y sólo han conseguido prevenir de forma efectiva que la web entera la utilice durante los próximos x años. Via Ajaxian.

13 marzo, 2009

Repaso a las nuevas bases de datos ligeras

  • Redis es una base de datos de clave-valor. Es similar a memcached pero el conjunto de datos no es volátil y los valores pueden ser cadenas (como memcached), pero también listas y conjuntos con operaciones atómicas para introducir y extraer elementos. Para ser muy rápido pero al mismo tiempo persistente, el conjunto completo de datos se mantiene en memoria y cada cierto tiempo o cuando hay un número suficiente de cambios en los datos se escribe de forma asíncrona al disco. Pueden perderse por tanto las últimas consultas lo que es aceptable en muchas aplicaciones aunque permite velocidades tan rápidas como las de una base de datos sobre memoria.
  • LightCloud es una base de datos distribuida y persistente basada en clave-valor. Construida sobre Tokyo Tyrant. Ofrece un gran rendimiento, almacena millones de claves en muy pocos servidores, fácil de escalar simplemente añadiendo nodos, tolerancia a fallos y balanceo de carga, extensible usando Lua, copias de seguridad y restauración en caliente, huella muy pequeña y programado sobre Python aunque extensible a cualquier otro lenguaje.
  • CouchDB es una base de datos distribuida, tolerante a fallos, sin esquema y orientada a documentos. Es accesible via una API RESTful sobre HTTP/JSON. Entre otras características, provee una replicación robusta, incremental con detección y resolución bidireccional de conflictos. Permite consultas e indexado utilizando un motor de vistas orientado a tablas con Javascript actuando como lenguaje de definición.
  • Google Big Table es un sistema de almacenamiento distribuido para gestionar datos estructurados diseñados para escalar a tamaños enormes: petabytes de datos repartidos sobre miles de servidores de mercado. Algunos proyectos de Google almacenan datos en Bigtable, incluyendo el indexado de webs, Google Earth, y Google Finance. Estas aplicaciones ubican demandas muy diferentes sobre Bigtable, tanto en términos del tamaño de los datos (desde URLs hasta páginas web hasta imágenes de satélite) y requerimientos de latencia (desde procesamiento retardado en grandes cantidades hasta servir datos en tiempo real).
  • Drizzle es una base de datos optimizada para aplicaciones de Red o en la nube. Se está diseñando para concurrencia masiva sobre arquitecturas modernas multi-núcleo. El código está derivado originalmente de MySQL y está enfocado en hacer una base de datos: fiable; rápida y escalable; diseño simple para facilitar la instalación y la gestión.
  • Amazon SimpleDB es un servicio web que provee funciones básicas de base de datos como indexado y consulta. Este servicio funciona en consonancia con el Amazon Simple Storage Service (Amazon S3) y con Amazon Elastic Compute Cloud (Amazon EC2), ofreciendo colectivamente la habilidad de almacenar, procesar, y consultar conjuntos de datos en la nube, facilitando la computación a escala web y resultando más económica para los desarrolladores.
  • Tokyo Cabinet es una biblioteca de rutinas para gestionar una base de datos. La base de datos es un simple archivo de datos conteniendo registros, cada uno de ellos es una tupla de clave y valor. Cada clave y valor es una cadena de bytes de longitud variable. Pueden usarse una cadena de carácteres o una binaria como clave o como valor. No hay concepto de tabla de datos ni de tipos de datos. Los registros se organizan en una tabla hash, árbol B+, o un array de longitud fija.

Addendum: Actualizo este artículo con el de Sentido Web que ofrece otras opciones:

Alternativas opensource de BigTable de Google:
Alternativas opensource de Amazon Dynamo, almacenamiento distribuido:
Otros proyectos interesantes:
  • MongoDB: almacenamiento de documentos
  • Scalaris: sistema de almacenamiento distribuido, escalable y transaccional

11 marzo, 2009

FlickrTrans 1.0.1 para Firefox 3.1 y posteriores

Con la nueva beta 2 de Firefox, ha cambiado la propiedad gContextMenu.imageURL; por gContextMenu.mediaURL; por lo que he actualizado la versión anterior. Esta nueva versión (1.0.1) soporta dicho cambio, por lo que será la válida para las nuevas versiones de Firefox a partir de la 3.1.

08 marzo, 2009

HTML 4.2: Arreglar o innovar

HTML 4.2, un artículo del gurú Douglas Crockford propone una idea excelente. Traduzco:

Me gusta la investigación y la experimentación. Me gusta el desarrollo de nuevas ideas y el redescubrimiento de las viejas ideas, y me gusta mezclar las cosas de forma creativa. El progreso proviene de la investigación. La investigación es buena.

Y me gustan los estándares. Los acuerdos y las convenciones sobre cómo funcionan las cosas y cómo encajan facilita efectos de cooperación beneficiosos, incluso habiendo competencia. La civilización se basa en las normas. Las normas son buenas.

Investigar y crear estándares a la vez es muy mala idea. Las normas deben ser estables y fiables. La investigación es desestabilizadora y arriesgada. Hacer ambas cosas es una receta para el desastre.

Hace unos años, ECMA TC-39 intentaba hacer las dos cosas juntas en un proyecto llamado ES4. Yo empecé un proyecto competidor llamado ES3.1. El nombre fraccional del proyecto indicaba que mi proyecto era menos ambicioso. En particular, su atención se centraba en la realización de las reparaciones necesarias para el estándar ECMAScript, y no en nuevas invenciones. ES3.1 probablemente se convierta en un estándar este mismo año.

Creo que el proyecto HTML5 está equivocado de la misma manera que el ES4. Se está tratando de hacer demasiado, sin una misión clara que defina los problemas que se pretenden solucionar. Creo que el proyecto necesita un reset. Quiero reenfocarlo con la intención de producir el estándar más pequeño posible, en lugar del estándar más grande posible.

Yo llamaría al proyecto de sustitución HTML4.2. Sería tentador llamarlo HTML4.1, pero el actual estándar HTML es el HTML 4.01, por lo que el potencial de confusión es demasiado grande.

Es fácil apilar características sobre otras características, pero eso, en última instancia, produce sistemas demasiado complejos, inseguros, y poco fiables. Un enfoque mucho más gratificante sería el de simplificar, racionalizar, generalizar y, aumentar el poder expresivo del sistema a la vez que se reduce su complejidad. Los buenos estándares requieren una disciplina enorme.

Últimos links en indiza.com