¿Por qué NO voy a utilizar Google Keep?

No-keep-Google-Keep

Justo después de anunciar el cierre de Reader, la todopoderosa Google presenta una nueva herramienta en forma de SaaS: Google Keep. Yo lo tengo claro. Yo tengo claro que no voy a usarla, y también tengo claro que algo ha cambiado en Google. Señores: torres más grandes han caído.

¿Y qué es Google Keep? No es más que una muy muy simple aplicación para gestionar notas, algo así como la versión electrónica de los clásicos Post-it! Lo primero que llama la atención es esa simplicidad extrema: ¿cuestiones de Time To Market o es más bien seguir fieles al estilo y estética que tanto éxito ha dado a la compañía? Pues tras hacer unas primeras pruebas, me inclino más por pensar que se trata de la primera motivación. De hecho opino que el producto no está minimamente maduro, y tengo mis fundamentos:

  • Al crear mi primera nota veo que existe la posibilidad de incluir imágenes. Lo único que se puede hacer es subir imágenes desde tu equipo, no es posible incluir imágenes desde un enlace. Pero por si esto fuera poco, la subida de imágenes no me funciona y me muestra un error invitándome a intentarlo más tarde.

Error al subir imagen

  • No veo por ninguna parte ninguna forma de relacionarse con otras aplicaciones, no tiene ninguna faceta social, y ni mucho menos una forma de exportar la información. Para mi son muy importantes todos estos aspectos, en la línea de lo que comentaba en mi anterior post. Si usas Google Keep estás aislado completamente. Sólo esto ya es motivo más que suficiente como para no usar esta herramienta sin vacilar.
  • Teniendo en cuenta que la herramienta está totalmente aislada, ¿qué ocurriría si de repente Google decide que ya no es una herramienta estratégica o rentable para la compañía? Mejor no pensarlo.
  • Intento destacar un texto en negrita… ¡imposible! No me lo permite, no tengo la opción. Sólo puedo usar texto plano tal cual. Casi que para eso prefiero usar un editor de textos de toda la vida.

En definitiva son razones más que de sobra, y más que de peso, para fundamentar mi idea de no usar Google Keep de ninguna manera, pero es más, empiezo a plantearme dejar de usar muchos más servicios de la compañía. Debo admitir que hasta el momento he sido todo un fan de Google y fiel usuario pero a día de hoy me lo estoy planteando y mucho:

  • En su día dejé Firefox para usar Chrome y últimamente me está dando problemas preocupantes. Se han empeñado en incluir su reproductor PepperFlash que no hace más que dar problemas y no funciona correctamente. Y lo peor de todo, si lo deshabilitas en favor del reproductor Flash de Adobe (que sí funciona como debe ser) al poco tiempo se vuelve a habilitar él solito. La vuelta a Firefox parece ser el mejor movimiento.
  • Drive me pareció una buena opción en su día hasta que he empezado a usarlo más intensivamente. Bastante a menudo me salta con errores que sólo dejan como opción cerrar la aplicación. Desde luego que Drive no puede ser mi herramienta de almacenamiento cloud de uso prioritario.

En definitiva, Google ya no es diferente, o sí lo es pero en el mal sentido. Ya no es innovadora ni ágil como lo fue en el pasado. Ha perdido el espíritu que la hizo grande. Sus nuevos servicios suelen ser copias de baja calidad de servicios ya existentes y además llenas de errores (da la sensación de estar siempre en fase beta). Constantemente dejan en la cuneta a sus usuarios, y no sólo por lo ocurrido con Reader (aparte de que han cerrado muchos más servicios, pregunto ¿qué fue de GWT?). Intento buscar alguna razón para usar Keep pero vaya, es que no encuentro ni una sola. Me reitero en lo dicho anteriormente, “señores, torres más grandes han caído“. Bienvenidos a la etapa oscura de Google.

El problema de la volatilidad de servicios en la nube: cómo conseguir la independencia

Cloud ComputingEl cierre de Google Reader ha sacado a flote de nuevo los miedos por la incertidumbre que nos crea la discontinuidad de una herramienta o servicio que estamos habituados a usar. Adaptas tus costumbres, tanto personales como profesionales, a una forma de trabajar durante mucho tiempo y de repente desaparece la herramienta. Igualmente ocurre con cualquier servicio en la nube, tanto PaaS, como SaaS o IaaS.  ¿Cómo te defiendes ante esto?

La clave para tu defensa está en establecer como prioridad máxima, dentro de tus factores de decisión, la futura independencia del proveedor. Para ello tienes que evaluar los siguientes aspectos:

  • Exportación y/o backup de datos en formato estándar: que tu información se encuentre guardada o bien la puedas recuperar en un formato 100% portable entre diferentes sistemas es fundamental. Es la única forma de asegurarte de que podrás explotar tus datos usando cualquier servicio. El rey indiscutible como estándar en este terreno es XML aunque existen alternativas a tener en cuenta como JSON, YAML, etc. y otros formatos según el tipo de archivo y/o dato que estemos utilizando.
    Como ejemplo, para el caso de Google Reader es posible realizar una exportación a un fichero OPML con la herramienta Google Takeout, que exporta en formato XML y JSON.
    También como ejemplo, en el servicio SaaS Google Docs existe la posibilidad de exportar la información en formato de Microsoft Office y OpenOffice entre otros. De no existir esta posibilidad seríamos esclavos del servicio.
  • Inter-compatibilidad entre servicios: siempre, en la medida de lo posible, será fundamental que elijas un proveedor cuyo servicio (protocolos, APIs, lenguajes soportados…) sea compatible con las ofertas de la competencia. De esta forma podrás cambiar de proveedor y por ejemplo migrar tus aplicaciones en cualquier momento y sin dificultad. En este sentido es importante, desde el punto de vista de desarrollo de software, usar siempre en tus desarrollos tecnologías estándar:
    • Desarrolla en Java, tendrás más puertas abiertas y menos limitaciones. Es el lenguaje más soportado en la nube. Como ejemplo, si desarrollas en PHP (soportado en Amazon EC2) no podrás portar tu aplicación a Google App Engine.
    • Utiliza APIs estándar, e intenta evitar en la medida de lo posible APIs propietarias de la plataforma, o te encontrarás con un problema. Como ejemplo, no es recomendable usar como método de autenticación cuentas de Google y su correspondiente API si estás usando Google App Engine. Mucho mejor será basarse en estándares como OpenID y OAuth.
  • Trata de no colocar todos tus huevos en la misma cesta: tu ubicuidad siempre será una ventaja para ti. Si usas varios proveedores al mismo tiempo estarás más preparado ante discontinuidades del servicio y serás más ágil a la hora de moverte y migrar de un sitio a otro si tienes problemas con un proveedor.Huevos en distintas cestas
    • Como ejemplo, en mi caso, para almacenamiento en la nube uso DropBox, Google Drive y SkyDrive al mismo tiempo. Puedo repartir y/o clonar mis datos entre ellos consiguiendo evidentes ventajas ante la posible discontinuidad o incluso caída del servicio de cualquiera de estos proveedores.
    • En cuanto a desarrollo de aplicaciones, la mejor forma de conseguir esto es usar la filosofía SOA en todo lo que hagas. Podrás mover servicios de un lugar a otro en la nube y conseguirás gran independencia, flexibilidad y escalabilidad. Aún más notable será la escalabilidad y eficiencia si optamos por un modelo REST en lugar de SOAP, siempre que las necesidades lo permitan.

Si tienes en cuenta estos aspectos, gracias al cloud computing podrás llegar a tocar el cielo de la independencia pues conseguirás entre otras ventajas:

  • Independencia de equipos dedicados: los problemas de CPU, memoria RAM, discos, etc. pasarán a la historia. Estos recursos te los proveerá dinámicamente la nube, aportando gran flexibilidad y escalabilidad.
  • Independencia en la supervisión: ya no será necesario estar pendiente de que tus servicios y aplicaciones estén funcionando correctamente. Tu proveedor se encargará de esto.
  • Independencia económica: ya no es necesario hacer grandes esfuerzos en inversiones iniciales para equipos que no sabemos bien cómo dimensionar.

Como conclusión, si partimos de la premisa de que el futuro está en la nube, para que la nube tenga futuro debe basar su desarrollo en estándares adoptados por toda la industria. Es muy sencillo: simplemente debe aprender de errores del pasado, como los cometidos por Internet Explorer. Todo lo que se salga de ahí, tanto por parte de los proveedores como de los consumidores, será como ponerse palos en las ruedas.

Integración de aplicaciones: creación de una correspondencia semántica de códigos mediante Excel

cabeceraUna tarea ineludible cuando integramos varias aplicaciones es la creación de diccionarios de códigos de forma que se pueda traducir la información, con diferentes códigos pero la misma semántica, entre sí. Un ejemplo típico podría ser el sexo: mientras una aplicación usa ‘H’ para identificar al varón, otra aplicación puede usar ‘V’. En este ejemplo es poco problemático construir una correspondencia semántica entre códigos, pero si tenemos que construir, por ejemplo, una correspondencia entre códigos de varios cientos de ciudades nos encontramos ante un problema.

Existen diversas herramientas avanzadas para solventar estos problemas, pero dentro de un desarrollo de recursos limitados (esto seguro que le suena a mucha gente) donde el uso de estas herramientas pueda resultar como matar moscas a cañonazos, resulta incuestionable la utilidad de las hojas de cálculo.

Vamos a partir de un ejemplo muy común y veremos la solución usando Excel. Supongamos que tenemos dos listados de provincias españolas. En uno de ellos las tenemos identificadas numéricamente según códigos postales; a esta hoja la llamaremos “CP”. En el otro listado, las provincias están identificadas con códigos no numéricos del ministerio de interior; a esta hoja la llamaremos “MI”. Finalmente queremos conseguir hacer la correspondencia y obtener algo como esto, que luego podríamos guardar en una tabla de nuestra aplicación o donde y de la forma que más nos convenga; a esta tercera hoja la podemos llamar “Resultado”.

La hoja de cálculo de partida sería la siguiente: HojaProvincias_Inicial

Al inspeccionar la hoja nos encontramos con los primeros típicos problemas:

  1. En un caso las provincias vienen con la primera letra mayúscula y en otro caso todo en mayúsculas.
  2. En un caso vienen incluídas las tildes y en otro no.
  3. La descripción no siempre coincide del todo.

El primer paso para solucionar estos problemas consiste en normalizar los literales. Lo pasaremos todo a mayúsculas, sin tildes o acentos y sin espacios sobrantes. Para el paso a mayúsculas la solución es sencilla, aplicar la fórmula MAYUSC de Excel. Después necesitamos eliminar las tildes. Existen varias formas de solventar este problema. La más simple, aplicando la siguiente fórmula nos quitamos los dos primeros problemas de un plumazo:

=ESPACIOS(SUSTITUIR(SUSTITUIR(SUSTITUIR(SUSTITUIR(SUSTITUIR(MAYUSC([celda]);"Á";"A");"É";"E");"Í";"I");"Ó";"O");"Ú";"U"))

Sustituyendo [celda] por la referencia a la celda que queremos transformar. En nuestro caso, con esto nos valdría como solución, pero en casos en los que tratemos con varios idiomas será necesaria una función más avanzada para hacer sustitución de tildes y otros caracteres. Una buena solución sería crear la siguiente función VBA en un módulo de nuestra hoja de cálculo:

Function SinAcentos(ByVal Celda As String) As String

Dim temp, strA$, strB$, i&, p&
strA = "áàäâãåçéèêëíìîïóòôöõð?úùûüýÿ?" & _
       "ÁÀÄÂÃÅÇÉÈÊËÍÌÎÏÓÒÔÖÕÐ?ÚÙÛÜÝ?? "
strB = "aaaaaaceeeeiiiioooooosuuuuyyz" & _
       "AAAAAACEEEEIIIIOOOOOOSUUUUYYZ"
temp = Celda

For i = 1 To Len(temp)
    p = InStr(strA, Mid(temp, i, 1))
    If p > 0 Then Mid(temp, i, 1) = Mid(strB, p, 1)
Next

SinAcentos = temp

End Function

Usando esta función podremos hacer cualquier transformación de un carácter por otro muy fácilmente, y aplicar la normalización en la columna C de cada hoja así:

=SinAcentos(ESPACIOS(MAYUSC([celda])))

La hoja Excel con las provincias normalizadas quedaria así: HojaProvincias_Normalizadas

Ahora que ya tenemos las descripciones de provincias normalizadas, nos queda el tercer problema para terminar por conseguir una solución: las descripciones no coinciden de forma exacta. Por ejemplo, tenemos “SANTA CRUZ DE TENERIFE” y “S. CRUZ DE TENERIFE”.

La primera solución que se puede venir a la cabeza es modificar a mano los registros que no coincidan. Para el ejemplo que tenemos, donde tratamos con alrededor de 50 registros puede ser la mejor opción, pero ¿lo haremos si disponemos de varios cientos o miles de registros? Para estos casos la solución ideal será aplicar el algoritmo de la distancia de Levenshtein. En la wikipedia se describe así:

En Teoría de la información y Ciencias de la Computación se llama Distancia de Levenshtein, distancia de edición, o distancia entre palabras, al número mínimo de operaciones requeridas para transformar una cadena de caracteres en otra. Se entiende por operación, bien una inserción, eliminación o la sustitución de un carácter.

Pues bien, implementamos en VBA el algoritmo de Levenshtein en nuestro módulo de Excel de la siguiente forma:

Function Levenshtein(ByVal string1 As String, ByVal string2 As String) As Long

Dim i As Long, j As Long
Dim string1_long As Long
Dim string2_long As Long
Dim distancia() As Long

string1_long = Len(string1)
string2_long = Len(string2)
ReDim distancia(string1_long, string2_long)

For i = 0 To string1_long
    distancia(i, 0) = i
Next

For j = 0 To string2_long
    distancia(0, j) = j
Next

For i = 1 To string1_long
    For j = 1 To string2_long
        If Asc(Mid$(string1, i, 1)) = Asc(Mid$(string2, j, 1)) Then
            distancia(i, j) = distancia(i - 1, j - 1)
        Else
            distancia(i, j) = Application.WorksheetFunction.Min _
            (distancia(i - 1, j) + 1, _
             distancia(i, j - 1) + 1, _
             distancia(i - 1, j - 1) + 1)
        End If
    Next
Next

Levenshtein = distancia(string1_long, string2_long)

End Function

Después necesitaremos una función que saque partido de este algoritmo y nos busque los resultados deseados. En la hoja que habíamos denominado “Resultado” tendremos referenciados los códigos postales y descripciones de provincias de la hoja “CP”. Necesitamos, por cada una de estas provincias, buscar entre las provincias que se encuentren en la hoja “MI” y devolver el código de aquella cuyo mejor resultado dé el algoritmo de Levenshtein.

Para ello lo ideal sería crear una nueva función VBA en nuestro módulo Excel a la que pasamos como parámetros la provincia que vamos a buscar, el rango de búsqueda, y dentro del rango la columna donde buscaremos los valores a comparar y finalmente la columna donde se encuentra el código resultante a devolver. Esta función sería algo así:

Function Correspondencia(ByVal string1 As String, ByVal rango As Range, ByVal columnaABuscar As Integer, ByVal columnaADevolver As Integer) As String

Dim i As Long
Dim levDist As Long
Dim levTemp As Long
Dim result As String

levDist = Len(string1)
For i = 1 To rango.Rows.Count
    levTemp = Levenshtein(string1, rango.Cells.Item(i, columnaABuscar))
    If levTemp = 0 Then
        result = rango.Cells.Item(i, columnaADevolver)
        Exit For
    Else
        If levTemp < levDist Then
            levDist = levTemp
            result = rango.Cells.Item(i, columnaADevolver)
        End If
    End If
Next

Correspondencia = result

End Function

Aplicamos esta función en la hoja de resultado usando referencias absolutas para definir el rango de celdas donde buscar; por ejemplo, en la celda C2 de la hoja “Resultado” tendríamos:

=Correspondencia(B2;MI!$A$2:$C$53;3;1)

Y de esta forma conseguimos el resultado final, que se puede descargar aquí: HojaProvincias_Resultado

Para terminar el post, una cuantas observaciones/comentarios:

  • Para que todo esto funcione es necesario habilitar las macros en Excel y habilitar el editor de Visual Basic, dependiendo de la versión instalada.
  • Aplicar el algoritmo de Levenshtein es algo que sólo se puede hacer si existe tolerancia a errores, pues con él no siempre se consiguen los resultados esperados.
  • Otra opción a considerar además de Levenshtein es el algoritmo n-gram para la detección de secuencias. En algunos casos puede ser incluso más recomendable que Levenshtein. Sería una mejora “para nota” crear un algoritmo que combine a ambos. Una versión fácilmente adaptable a VBA del algoritmo n-gram se encuentra aquí.

Hello world!

hello_worldCuriosamente justo al crear un nuevo blog en WordPress.com la primera entrada que sugiere automáticamente se titula “Hello World!”.  Y digo yo: ¿qué mejor título que éste para un primer post en un blog sobre desarrollo de software (aunque aparezcan 2 posts anteriores, éste se puede considerar el primer post ya que los otros 2 son rescatados de un antiguo blog)?

Pues bien, hurgando en el baúl de los recuerdos, se me antoja un agradable ejercicio de nostalgia hacer un repaso sobre los “Hello World!” que he visto y experimentado en mis propias carnes desde que empecé en el mundo de la programación allá por el año…

1986: el primer ordenador que tuve en mis manos fue un Commodore Vic20. El primero que fue mío, un MSX de Sanyo. Ambos arrancaban con un intérprete de Basic:

10 print "Hello World!"

¡Qué sencillo era programar! Dediqué incontables horas a picar los listados de programas que publicaban en revistas de la época. Después los guardaba en cintas de cassette y los ejecutaba con un “RUN”.

¡Qué tiempos! Mis amigos tenían Spectrum 48K, Commodore 64K, Amstrad CPC464… y yo un MSX de 64K con monitor de fósforo verde!

1988: este año empecé con el ensamblador del Z-80. El Basic se me quedó lento y quería sacar el máximo rendimiento a la máquina. Ahora soy incapaz de escribir de memoria un “Hello World!” en ensamblador pero buscando en la web pude encontrar algo:

 EQU CR    = #0D          ; carriage return
 EQU PROUT = #xxxx        ; character output routine
 ;
         LD  HL,MSG       ; Point to message
 ;
 PRLOOP  LD  A,(HL)       ; read byte from message
         AND A            ; set zero flag from byte read
         RET Z            ; end of text if zero
         JSR PROUT        ; output char
         INC HL           ; point to next char
         JR  PRLOOP       ; repeat
 ;
 MSG     DB  "Hello world!",CR,00
 ;

¡Qué recuerdos! Básicamente lo que hace es guardar un puntero al mensaje en el registro HL y luego va cargando uno a uno los caracteres a imprimir en el acumulador y los va imprimiendo hasta encontrar un cero, momento en el que el programa termina y retorna.

1990: en este año conseguí ser el propietario de un flamante ordenador PC con procesador Intel 80386. ¡Guau! El MSX quedaba atrás y empezaba con palabras mayores. Así, con un buen manual y esa máquina, pude aprender MS-DOS:

echo Hello world!

1992: en octubre de este año ingresé en la universidad para estudiar informática. En el primer curso aprendimos Pascal. Para mi fue una grata sorpresa aprender este lenguaje, teniendo en cuenta que sólo conocía el Basic y el ensamblador Z-80; aquello de la programación estructurada resultaba genial:

program HelloWorld;
begin
  WriteLn('Hello World!')
end.

1993: ya en el segundo año en la universidad aprendimos C. Me pareció aún más interesante que Pascal. Aún podía usar técnicas de programación estructurada pero con accesos de bajo nivel que me recordaban el potencial del ensamblador:

#include <stdio.h>
int main(int argc, char *argv[])
{
 printf("Hello world!\n");
}

Varios dolores de cabeza tuve con punteros perdidos, memorias no liberadas…

1994: el tercer año en la universidad aprendimos bastante sobre bases de datos, y esto me dio la oportunidad de conocer SQLPL/SQL:

SET SERVEROUTPUT ON;
BEGIN
    DBMS_OUTPUT.PUT_LINE('Hello world!');
END;

1995: ya en cuarto curso, gracias a la asignatura de Inteligencia Artificial, pude conocer nuevos lenguajes. Primero fue LISP (menudo lío de paréntesis):

(princ "Hello world!")

y después vino Prolog:

main :- write('Hello world!'), nl.

También fue ese año cuando conocí lo que para mi era un nuevo e interesantísimo concepto, la programación orientada a objetos, y fue con Smalltalk y la torre de Hanoi:

Transcript show: 'Hello world!'.

1996: este año conocí el CGI scripting con Perl. Aquello sí que me maravilló ¡era posible construir páginas web con contenido dinámico!

print "Hello World!\n";

1997: un curso de verano en la universidad me hizo descubrir Javascript. De nuevo estaba maravillado: las páginas web podían ser dinámicas gracias a los scripts CGI y además era posible ejecutar código en cliente!

document.write('Hello world!');

Luego ya vendrían los problemas de compatibilidad entre navegadores etc. pero eso ya es otra guerra… por entonces usábamos el timón de Netscape.

1999: andaba este año buscando mi primer empleo como programador, y una empresa me dio la oportunidad de aprender primero Visual Basic:

MsgBox "Hello, world!"

He de reconocer que me gustó. El lenguaje no era extraño para mi y me daba nuevas e interesantes posibilidades.
A continuación topé con Java por primera vez:

public class HelloWorld {
   public static void main(String[] args) {
       System.out.println("Hello world!");
   }
}

Java me recordaba mucho a C y me enamoró en seguida (y aún lo estoy): escribía un código que funcionaba en todas las plataformas, con programación a la vez estructurada y orientada a objetos, y sin los problemas y dolores de cabeza que ocasionaba C. ¡Eran todo ventajas!

2003: en esta ocasión se me ocurrió desarrollar un proyecto personal, una página web, dinámica por supuesto. Sopesé las opciones que se me planteaban y finalmente decidí que la herramienta ideal para este desarrollo era PHP:

<!--?php print 'Hello world' ?-->

Se me antojaba un extraño potaje entre HTML y programación y de primeras no me terminó de convencer. Aún así, como es habitual en mi, una nueva herramienta, un nuevo reto y un nuevo aprendizaje eran una motivación añadida.

2006: en este año tenía entre manos el desarrollo de una intranet corporativa y Microsoft Sharepoint 2003 resultó ser una gran herramienta para ello, motivo por el cual tuve mi primer contacto con C#, que luego continué con otros proyectos personales:

using System;
class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello world!");
    }
}

Seguramente se me haya quedado algún lenguaje o herramienta por el camino. ¿Por dónde continuar? Pues por ejemplo por Python, Groovy, Objective-C, Ruby, …

Como reflexión, vistos todos estos años con la perspectiva de la experiencia acumulada, el que se ha mantenido ahí siempre y sigo usando a día de hoy es Java, y aún no está muerto en absoluto. Si bien está claro que no es el lenguaje ideal para todo (de hecho no existe tal lenguaje) sí ha quedado claro su gran potencial y que ha sabido evolucionar con el paso de los años sin quedar obsoleto.