martes, 10 de enero de 2017

Código Buggy Python: Los 10 Errores más Comunes que Cometen los Desarrolladores Python



Acerca de Python

Python es un lenguaje de programación interpretado y orientado a objetos de alto nivel con semántica dinámica. Su alto nivel integrado en las estructuras de datos, combinado con escritura y binding dinámicos lo hacen muy atractivo para el desarrollo rápido de aplicaciones, así como para su uso como lenguaje de script o glue para conectar componentes o servicios existentes. Python trabaja con módulos y paquetes, fomentando así la modularidad del programa y la reutilización de código.

Sobre este Artículo

La sintaxis simple y fácil de aprender de Python, puede enviar a los desarrolladores de Python en la dirección incorrecta - especialmente aquellos que están conociendo la lengua – perdiendo en el camino algunas de sus sutilezas y subestimando el poder del lenguaje diverso de Python.
Con esto en mente, este artículo presenta una lista “top 10” de errores sutiles, y difíciles de ver, que pueden tomar desprevenidos incluso a algunos de los desarrolladores de Python más avanzados.
(Nota: Este artículo está dirigido a un público más avanzado que el de Errores Comunes de Programadores Python, que se orienta más hacia aquellos que son nuevos en la lengua.)

Error común # 1: Un Mal Uso de Expresiones como Valores Predeterminados para los Argumentos de la Función

Python permite especificar que un argumento de la función es opcional, proporcionando un valor por defecto para ello. Si bien ésta es una gran característica de la lengua, puede dar lugar a cierta confusión cuando el valor por defecto es mutable. Por ejemplo, considera ésta definición de la función de Python:
>>> def foo(bar=[]):        # bar is optional and defaults to [] if not specified
...    bar.append("baz")    # but this line could be problematic, as we'll see...
...    return bar
Un error común es pensar que el argumento opcional se establecerá en la expresión por defecto específica, cada vez que se llama la función sin necesidad de suministrar un valor para el argumento opcional. En el código anterior, por ejemplo, se podría esperar que llamar a foo() varias veces (es decir, sin especificar un argumento bar) siempre daría de regreso baz, ya que la hipótesis sería que cada vez que foo() se llama (sin un argumento bar especificado) bar está ajustado a [] (es decir, una nueva lista vacía).
Pero vamos a ver lo que realmente sucede cuando se hace esto:
>>> foo()
["baz"]
>>> foo()
["baz", "baz"]
>>> foo()
["baz", "baz", "baz"]
¿Eh? ¿Por qué se siguió añadiendo el valor predeterminado de baz a una lista existente cada vez que foo() era llamado, en lugar de crear una nueva lista en cada oportunidad? La respuesta más avanzada de programación Python es que, el valor por defecto para un argumento de función se evalúa sólo una vez, en el momento en que se define la función. Por lo tanto, el argumento bar se inicializa con su valor por defecto (es decir, una lista vacía) sólo cuando foo() se ha definido por primera vez, pero luego los llamados a foo() (es decir, sin un argumento bar especificado) seguirán utilizando la misma lista a la que bar fue inicializado originalmente.
Por cierto, una solución común para esto es la siguiente:
>>> def foo(bar=None):
...    if bar is None:  # or if not bar:
...        bar = []
...    bar.append("baz")
...    return bar
...
>>> foo()
["baz"]
>>> foo()
["baz"]
>>> foo()
["baz"]

Error común # 2: Uso Incorrecto de las Variables de Clase

Consideremos el siguiente ejemplo:
>>> class A(object):
...     x = 1
...
>>> class B(A):
...     pass
...
>>> class C(A):
...     pass
...
>>> print A.x, B.x, C.x
1 1 1
Tiene sentido.
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
Sí, de nuevo como se esperaba.
>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
¿Qué es esto? Sólo cambiamos A.x ¿Por qué C.x cambió también?
En Python, las variables de clase se manejan internamente como diccionarios y siguen lo que se refiere a menudo como Method Resolution Order (MRO). Así que en el código anterior, ya que el atributo x no se encuentra en la clase C, se buscará en sus clases base (únicamente A en el ejemplo anterior, aunque Python apoya herencia múltiple). En otras palabras, C no tiene su propia propiedad x, independiente de A. Por lo tanto, las referencias a C.x son de hecho referencias a A.x. Esto causa un problema Python, a menos que se maneje adecuadamente. Más información sobre atributos de clase en Python.

https://www.toptal.com/python/c%C3%B3digo-buggy-python-los-10-errores-m%C3%A1s-comunes-que-cometen-los-desarrolladores-python/es

Error común # 3: Especificación de Parámetros de Forma Incorrecta para un Bloque de Excepción

Supongamos que tienes el siguiente código:
>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except ValueError, IndexError:  # To catch both exceptions, right?
...     pass
...
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
IndexError: list index out of range
El problema aquí es que el informe except no toma una lista de excepciones que se especifiquen de esta forma. Por el contrario, Python 2.x la sintaxis except Exception, e, se utiliza para enlazar la excepción al segundo parámetro opcional especificado (en éste caso e), con el fin de que esté disponible para una inspección adicional. Como resultado, en el código anterior, la excepción IndexError no está siendo capturada por el informe except; más bien, la excepción termina siendo enlazada a un parámetro llamado IndexError.
La forma correcta de capturar varias excepciones en un informe except, es especificar el primer parámetro como una tupla que contiene todas las excepciones a ser capturadas. Además, para la máxima portabilidad, utiliza la palabra clave as, ya que la sintaxis es apoyada por Python 2 y Python 3:
>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except (ValueError, IndexError) as e:  
...     pass
...
>>>

Error común # 4: No Entender las Reglas de Ámbito de Python

La resolución de ámbito de Python se basa en lo que se conoce como la regla LEGB, que es la abreviatura de Local, Enclosing, Global, Built-in. Parece bastante sencillo, ¿verdad? Bueno, en realidad, hay algunas sutilezas en la forma en que esto funciona en Python, lo que nos lleva al problema común, más avanzado, de programación Python, a continuación.
Considera lo siguiente:
>>> x = 10
>>> def foo():
...     x += 1
...     print x
...
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment
¿Cuál es el problema?
El error anterior se debe a que, cuando se hace una asignación a una variable en un ámbito, esa variable es considerada, automáticamente por Python, como local en ese ámbito y sigue cualquier variable de nombre similar, en cualquier ámbito exterior.
Muchos de ellos son, por lo tanto, se sorprenden al conseguir un UnboundLocalError en el código de trabajo anterior, cuando éste se modifica al añadir una instrucción de informe, en alguna parte del cuerpo de una función. (Puedes leer más sobre esto aquí.)
Es particularmente común que esto confunda a los desarrolladores cuando usan listas. Considera el siguiente ejemplo:
>>> lst = [1, 2, 3]
>>> def foo1():
...     lst.append(5)   # This works ok...
...
>>> foo1()
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2():
...     lst += [5]      # ... but this bombs!
...
>>> foo2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment
¿Eh? ¿Por qué foo2 falló, mientras que foo1 funcionó muy bien?
La respuesta es la misma que en el problema del ejemplo anterior, pero es sin duda más sutil. foo1 No está haciendo una asignación a lst, mientras que foo2 sí lo está. Recordando que lst += [5] es en realidad la abreviatura de lst = lst + [5], vemos que estamos tratando de asignar un valor a lst (por lo tanto, Python presume que está en el ámbito local). Sin embargo, el valor que estamos tratando de asignar a lst se basa en el mismo lst (de nuevo, ahora presume estar en el ámbito local), que aún no ha sido definido. Boom.

Error común # 5: Modificar una Lista al Iterar Sobre Ella

El problema con el siguiente código debería ser bastante obvio:
>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> for i in range(len(numbers)):
...     if odd(numbers[i]):
...         del numbers[i]  # BAD: Deleting item from a list while iterating over it
...
Traceback (most recent call last):
     File "<stdin>", line 2, in <module>
IndexError: list index out of range
La eliminación de un elemento de una lista o matriz, mientras que se da iteración sobre ella, es un problema de Python que es bien conocido por cualquier desarrollador de software con experiencia. Sin embargo, aunque el ejemplo anterior puede ser bastante obvio, incluso los desarrolladores avanzados pueden involuntariamente, ser tomados por sorpresa por éste código, el cual es mucho más complejo.
Afortunadamente, Python incorpora una serie de paradigmas de programación elegantes que cuando se utilizan correctamente, pueden resultar en un código significativamente simplificado y racionalizado. Un beneficio secundario de esto es que el código más simple es menos probable que sea sorprendido por el bug eliminación-accidental-de-un-item-de-lista-mientras-iterando-sobre-ella. Uno de estos paradigmas es el de [comprensiones de lista]((https://docs.python.org/2/tutorial/datastructures.html#tut-listcomps). Por otra parte, las comprensiones de lista son particularmente útiles para evitar este problema específico, como se muestra en ésta implementación alternativa del código mostrado arriba, que funciona perfectamente:
>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> numbers[:] = [n for n in numbers if not odd(n)]  # ahh, the beauty of it all
>>> numbers
[0, 2, 4, 6, 8
Error común # 6: La Confusión de Cómo Python Enlaza las Variables en los Cierres
Teniendo en cuenta el siguiente ejemplo:
>>> def create_multipliers():
...     return [lambda x : i * x for i in range(5)]
>>> for multiplier in create_multipliers():
...     print multiplier(2)
...
Deberías esperar el siguiente resultado:
0
2
4
6
8
Pero en realidad obtienes:
8
8
8
8
8
¡Sorpresa!
Esto sucede debido al comportamiento enlace tardío de Python, que dice que los valores de las variables utilizadas en los cierres, se buscan en el momento en el que se llama a la función interna. Así que en el código anterior, cuando cualquiera de las funciones devueltas es llamada, el valor de i se busca en el ámbito que lo rodea en el momento en que se llama (y en ese momento, el círculo se ha completado, por lo que a i ya se le ha asignado su valor final de 4).
La solución a este problema común Python es un poco de hack:
>>> def create_multipliers():
...     return [lambda x, i=i : i * x for i in range(5)]
...
>>> for multiplier in create_multipliers():
...     print multiplier(2)
...
0
2
4
6
8
¡Voilà! Estamos tomando ventaja de los argumentos por defecto para generar funciones anónimas, con el fin de lograr el comportamiento deseado. Algunos llamarían a esto, elegante. Algunos lo llamarían sutil. Algunos lo odian. Pero si eres un desarrollador de Python, es importante entender esto.

Error común # 7: Crear Dependencias de Módulos Circulares

Digamos que tienes dos archivos, a.py y b.py, cada uno de los cuales importa al otro, de la siguiente manera:
en a.py:
import b

def f():
    return b.x
 
print f()
Y en b.py:
import a

x = 1

def g():
    print a.f()
En primer lugar, vamos a tratar de importar a.py:
>>> import a
1
Funcionó muy bien. Tal vez te sorprendió. Después de todo, tenemos una importación circular aquí que presumiblemente debería ser un problema, ¿no?
La respuesta es que la mera presencia de una importación circular no es como tal un problema en Python. Si un módulo ya se ha importado, Python es lo suficientemente inteligente como para no intentar volverlo a importar. Sin embargo, dependiendo del punto en el que cada módulo está intentando acceder a las funciones o variables definidas en el otro, podrías tener problemas.
Así que volviendo a nuestro ejemplo, cuando importamos a.py, no tenía ningún problema al importar b.py, ya que b.py no requiere nada de b.py para definirse en el momento de su importación. La única referencia en b.py a a, es el llamado a a.f(). Pero ese llamado está en g() y nada en a.py o b.py invoca g(). Así que, la vida es bella.
Pero ¿qué ocurre si se intenta importar b.py (claro, sin haber previamente importado a.py):
>>> import b
Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
     File "b.py", line 1, in <module>
    import a
     File "a.py", line 6, in <module>
 print f()
     File "a.py", line 4, in f
 return b.x
AttributeError: 'module' object has no attribute 'x'
Uh oh. ¡Eso no es bueno! El problema aquí es que, en el proceso de importación b.py, intenta importar a.py, lo que como resultado llama a f(), que a su vez intenta acceder a b.x. Pero b.x aún no ha sido definida. De ahí la excepción AttributeError.
Al menos una solución a esto es bastante trivial. Simplemente modifica b.py para importar a.py dentro de g():
x = 1

def g():
    import a # This will be evaluated only when g() is called
    print a.f()
Cuando se importa, todo está bien:
>>> import b
>>> b.g()
1 # Printed a first time since module 'a' calls 'print f()' at the end
1 # Printed a second time, this one is our call to 'g'

Error común # 8: Choque de Nombres con Módulos de la Biblioteca Estándar de Python

Una de las ventajas de Python es la gran cantidad de módulos de biblioteca que trae desde el principio. Pero como resultado, si no estás evitando esto conscientemente, no es tan difícil encontrarse con un choque de nombres, entre el nombre de uno de tus módulos y un módulo con el mismo nombre en la biblioteca estándar que se incluye en Python (por ejemplo, es posible que tengas un módulo denominado email.py en tu código, lo que estaría en conflicto con el módulo de biblioteca estándar del mismo nombre).
Esto puede conducir a problemas muy agresivos, como la importación de otra biblioteca que a su vez intenta importar la versión de bibliotecas estándar Python de un módulo, pero como ya tienes un módulo con el mismo nombre, el otro paquete importa erróneamente tu versión, en lugar de la que se encuentra dentro de la biblioteca estándar Python, y es aquí es donde se producen los errores más graves.
Por lo tanto, se debe tener cuidado para evitar el uso de los mismos nombres que los de los módulos de biblioteca estándar Python. Es mucho más fácil para ti cambiar el nombre de un módulo dentro de tu paquete que presentar una propuesta de mejora de Python (PEP) para solicitar un cambio de nombre upstream y conseguir que lo aprueben.

Error común # 9: El No Poder Hacer Frente a las Diferencias entre Python 2 y Python 3

Considera el siguiente archivo foo.py:
import sys

def bar(i):
    if i == 1:
        raise KeyError(1)
    if i == 2:
        raise ValueError(2)

def bad():
    e = None
    try:
        bar(int(sys.argv[1]))
    except KeyError as e:
        print('key error')
    except ValueError as e:
        print('value error')
    print(e)

bad()
En Python 2, esto funciona muy bien:
$ python foo.py 1
key error
1
$ python foo.py 2
value error
2
Pero ahora vamos a dar un giro en Python 3:
$ python3 foo.py 1
key error
Traceback (most recent call last):
  File "foo.py", line 19, in <module>
    bad()
  File "foo.py", line 17, in bad
    print(e)
UnboundLocalError: local variable 'e' referenced before assignment
¿Qué acaba de ocurrir aquí? El “problema” es que en Python 3 el objeto de excepción no es accesible más allá del alcance del bloque except. (La razón de esto es que, de lo contrario, mantendría un ciclo de referencia con el marco de pila en la memoria hasta que se ejecute el recolector de basura y purgue las referencias de la memoria. Más detalles técnicos sobre esto están disponibles aquí).
Una forma de evitar este problema es mantener una referencia al objeto de excepción fuera del alcance del bloque except, de modo que siga siendo accesible. Aquí hay una versión del ejemplo anterior que utiliza esta técnica, por lo tanto, dosificando el código y haciéndole más compatible con Python 2 y Python 3:
import sys

def bar(i):
    if i == 1:
        raise KeyError(1)
    if i == 2:
        raise ValueError(2)

def good():
    exception = None
    try:
        bar(int(sys.argv[1]))
    except KeyError as e:
        exception = e
        print('key error')
    except ValueError as e:
        exception = e
        print('value error')
    print(exception)

good()
La ejecución de éste es en Py3k:
$ python3 foo.py 1
key error
1
$ python3 foo.py 2
value error
2
¡Yupi!
(Por cierto, nuestra Guía de contratación Python (Python Hiring Guide) analiza una serie de diferencias importantes, que se deben tener en cuenta al migrar el código de Python 2 a Python 3.)

Error común # 10: El Mal Uso del Método __del__

Digamos que tenías esto en un archivo llamado mod.py:
import foo

class Bar(object):
        ...
    def __del__(self):
        foo.cleanup(self.myhandle)
Y, luego, trataste de hacer esto desde another_mod.py:
import mod
mybar = mod.Bar()
Obtendrías una fea excepción AttributeError.
¿Por qué? Debido a que, como se informó aquí, al apagarse intérprete, las variables globales del módulo se ajustan a None. Como resultado, en el ejemplo anterior, en el punto que __del__ se invoca, el nombre foo ya se ha ajustado a None.
Una solución a este problema algo más avanzado que la programación Python, sería utilizar atexit.register() en su lugar. De esta manera, cuando el programa se haya ejecutado (al salir normalmente, quiero decir), tus gestores registrados son echados antes de que el intérprete se apague.
Con éste conocimiento, una solución para el anterior código mod.py podría ser algo como esto:
import foo
import atexit

def cleanup(handle):
    foo.cleanup(handle)


class Bar(object):
    def __init__(self):
        ...
        atexit.register(cleanup, self.myhandle)
Esta aplicación ofrece una manera limpia y confiable de llamar a cualquier funcionalidad de limpieza necesaria para después de la terminación normal del programa. Obviamente, le toca a foo.cleanup decidir qué hacer con el objeto unido al nombre self.myhandle, pero se entiende la idea.

Para Terminar

Python es un lenguaje potente y flexible con muchos mecanismos y paradigmas que pueden mejorar considerablemente la productividad. Sin embargo, al igual que con cualquier herramienta de software o lenguaje, el tener una limitada comprensión o apreciación de sus capacidades a veces puede ser más un impedimento que una ventaja, dejándonos en el estado proverbial de “saber lo suficiente como para ser peligroso”.
Familiarizándose con los matices clave de Python, tales como (pero de ninguna manera se limita a) los problemas de programación moderadamente avanzados planteados en este artículo, ayudará a optimizar el uso de la lengua, evitando algunos de sus errores más comunes.
Deberías revisar nuestra Guía a Entrevistas Python (Insider’s Guide to Python Interviewing), para obtener sugerencias sobre preguntas de entrevistas que pueden ayudar a identificar a los expertos en Python.
Esperamos que te sean útiles los consejos en este artículo y apreciamos tus comentarios

jueves, 18 de agosto de 2016

PLATAFORMAS PARA EL DESARROLLO DE APLICACIONES WEB



Diana Shirley Sanchez Hernandez
Electiva Profesional A3
FUSM - Fundación Universitaria San Martin
Palmira-Colombia
preciosanchez@gmail.com



ResumenEste ensayo nos dará una idea general acerca de las plataformas utilizadas para crear y/o desarrollar aplicaciones web, identificaremos los principales lenguajes de programación  (o plataformas de desarrollo) para la implementación aplicaciones web y los servidores (de aplicaciones) en los cuales se instalan dichas aplicaciones (por lenguaje de programación).

Índice de Términos: Plataforma de desarrollo, Aplicaciones web, programación, implementación.


I.                    INTRODUCCIÓN

En la actualidad Internet es la mayor fuente de contenidos donde los usuarios pueden buscar información sobre lo que necesitan. Los motores de búsqueda Como Google son los encargados de esta actividad, que generalmente dan como resultado un exagerado volumen de información, tan grande que encontrar la que se ajuste a las necesidades personales podría consumir mucho tiempo del cual no se dispone.
Los sistemas de búsqueda basan su proceso en fórmulas matemáticas y las coincidencias de las palabras escritas por el usuario en el texto de los documentos encontrados.

El objetivo de este trabajo es estudiar la tecnología necesaria que propone una Solución a los problemas que hoy aquejan nuestro sistema obsoleto a la hora de realizar cualquier actividad de la vida diaria.

Este trabajo inicia con una pequeña introducción sobre conceptos de Lenguajes de  programación,
Luego la unidad 2 muestra la implementación de las diferentes aplicaciones web, y los servidores en los que se instalaran dichas aplicaciones.

CRITERIOS PARA EVALUAR UNA PLATAFORMA DE DESARROLLO.

A continuación las 5 plataformas más populares de lado servidor: Java, PHP, Microsoft .NET, Python y Ruby On



Rails. Las plataformas se evaluaran en base a los siguientes Criterios:

         Grado de madurez.
         Tamaño y grado de actividad de la comunidad.
         Disponibilidad de librerías y aplicaciones de terceros.
         Disponibilidad y coste salarial de los programadores.
         Dificultad de la curva de aprendizaje.
         Compatibilidad con el resto del ecosistema.
         Rendimiento y escalabilidad.

La productividad como ya es sabido si se usan bien, todas ellas ofrecen un grado de productividad equivalente, o, al menos no lo bastante diferente como para que debiera ser relevante.

¿POR QUÉ?

LA MADUREZ

 Hay proyectos, como por ejemplo node.js que se crearon para resolver problemas específicos. En el caso de node.js el tratamiento de peticiones AJAX desde las nuevas aplicaciones HTML5. Node.js ofrece una modalidad de procesamiento asíncrono junto con la propuesta de programar en JavaScript igualmente en el lado cliente y en el servidor
Es más complicado  de aprender y de codificar en Tomcat, ciertamente, pero a cambio se tiene una plataforma mucho más madura, eficiente y compatible que node.js. Más madura implica, por ejemplo, casi siempre mejor documentada. Las carencias de la documentación, por cierto, son un mal endémico en las librerías JavaScript cliente incluso en las más maduras como Dojo o YUI.

EL TAMAÑO

Es relevante porque cuanto mayor sea la comunidad más probable es que hay alguien que se haya topado con el mismo problema que nosotros y lo haya resuelto. Más probable que en Stack Overflow o en cualquier otro sitio de preguntas y respuestas hallemos la solución a un problema que nos mantiene atascados.


LA DISPONIBILIDAD

De aplicaciones y librerías de terceros. El framework de desarrollo es sólo la herramienta base para construir una aplicación. Justo a continuación de elegir la plataforma hay que elegir las librerías. Por ejemplo, Java domina absolutamente el entorno financiero y bancario porque su middleware es el mejor. Lo hay para todos los gustos desde JBoss a TIBCO, pasando por Informatica UM. Los frameworks más modernos de todos los lenguajes han ido incorporando librerías de serie. Java apareció en 1995 y, más que un lenguaje para aplicaciones web estaba pensado con filosofía WORA (Write Once Run Anywhere) y para tener la menor cantidad de dependencias posible. Ruby On Rails apareció 10 años después e incorporaba en su arquitectura el paradigma Modelo Vista Controlador (MVC) y mecanismos como ActiveRecord que permiten que las clases tomen de la base de datos los nombres de la columnas sin necesidad de definirlas explícitamente en el código. Para conseguir con Java un framework funcionalmente similar a Ruby on Rails se necesita, al menos, una librería MVC como Struts, una capa de persistencia como Hibernate, soporte de servicios REST como Jersey, una librería de tags como JSTL y JavaMail. La cantidad de librerías Java es tal que existen empresas como Black Duck en EE.UU. Autentia en España especializadas en evaluarlas y dar formación a los clientes corporativos.

DISPONIBILIDAD Y COSTE

 Si se estima que el proyecto requerirá 40 o 50 desarrolladores entonces encontrar y contratar personas puede llegar a ser muy difícil y costoso. Si se estima que el proyecto necesitará más de 50 desarrolladores, entonces es que se ha hecho mal el plan y bien hay que partir el proyecto en trozos de menos de 50, bien sería mejor cancelarlo y tirarlo todo a la basura antes de que se queme una burrada de dinero en producir un monstruo.

DIFICULTAD DE LA CURVA DE APRENDIZAJE.

Todas las plataformas son igualmente buenas en cuanto a productividad. Pero no todas son igualmente fáciles de aprender. Si se tiene cuenta el tiempo que requiere un programador novato para aprender cómo funciona el invento, entonces sí que existen diferencias entre unas y otras. El orden de más fácil a más difícil es: PHP, Ruby On Rails, .NET, Python y Java.

COMPATIBILIDAD CON EL RESTO DEL ECOSISTEMA.

Rara vez las aplicaciones existen de forma aislada e independiente unas de otras. En general, las diferentes plataformas no son fácilmente interoperables unas con otras. Existen muchas herramientas de integración y formatos de representación de datos independientes del lenguaje como Protocol Buffers, pero lo más común, por fácil, es que las plataformas en dos lenguajes diferentes se acaben integrando vía base de datos relacional, XML o ficheros de texto delimitado.

RENDIMIENTO Y ESCALABILIDAD.

A grosso modo, si se tiene una base de datos con tablas que no superan el millón de registros y un sitio web que no supera las cien mil visitas al día, entonces la escalabilidad no será una cuestión relevante se use lo que se use a menos que el programador sea realmente malo, pues cualquier servidor quad-core medio moderno puede atender tal volumen de trabajo sobre una base de datos relacional.


II.                   LENGUAJES

JAVA

Java es la plataforma más extendida en el entorno corporativo. Se trata de una tecnología muy madura y popular que cuenta con innumerables herramientas de todo tipo y es bastante sencillo encontrar programadores. Casi todo el mundo que desarrolla en Java usa Eclipse o Netbeans como IDE. Ambos son bastante pesados y a mi personalmente me gusta más JCreator, aunque en un portátil con 4Gb de RAM tanto Eclipse como Netbeans corren perfectamente. La principal pega de Java para el desarrollo de aplicaciones web es que la plataforma no se concibió originalmente para eso, sino que fueron apareciendo proyectos como Tomcat en 1999. Las extensiones a la plataforma se acuerdan mediante el Java Community Process (JCP) compuesto por más de mil miembros que trabajan sobre más de 300 Java Specification Requests (JSR) Estas especificaciones tienden a ser muy densas y a veces salen cosas realmente retorcidas como JavaMail. Algunos presuntos gurús han difundido el mito de que una startup no debería basar su tecnología en Java. Esto es radicalmente falso pues Java es perfectamente compatible con el modelo lean en boga. La curva de aprendizaje de Java no es suave.

PHP

También conocido popularmente como LAMP (Linux+Apache+MySQL+PHP). PHP (acrónimo recursivo de Hypertext Pre-processor) es coetáneo de Java. Apareció también en 1995, pero, a diferencia de Java, estaba pensado desde el principio como un lenguaje que se pudiera incorporar en documentos HTML. La gran ventaja de PHP es que resulta sencillo empezar con él y existe mucha documentación online. Es más fácil aprender PHP que Java, y todos los ISP proprocionan algún tipo de stack LAMP preconfigurado. Es por consiguiente sencillo encontrar programadores PHP a precios asequibles. PHP no proporciona un sistema MVC por defecto pero existen muchas opciones para ello. A mi me gusta CakePHP mas se pueden contar las alternativas por decenas siendo Zend probablemente la más popular. Hay muchas startups de éxito basadas en PHP, incluídas algunas de las de mayor tráfico como Wikipedia, Yahoo o Facebook entre ellas. El lenguage en sí mismo presenta algunas limitaciones importantes. No se pueden crear de forma natural pools de conexiones, no hay sesiones, el módulo mod_php para Apache permite mantener sesiones pero mucha gente lo considera intrínsecamente inseguro. La propia facebook acabó desarrollando su propio compilador just in time (JIT) HHVM para poder alcanzar el rendimiento que necesitaban con PHP.

.NET

Si Sun no hubiese creado Java probablemente .NET dominaría ahora mismo todo el panorama de software empresarial. El punto fuerte de plataforma de Microsoft es el grado de integración entre el escritorio y las aplicaciones web. Si yo tuviese que desarrollar una Intranet a sabiendas de que los usuarios van a tener todos Windows no lo dudaría y la desarrollaría sobre .NET. Mas para una startup yo nunca elegiría .NET no por problema alguno con la tecnología sino simplemente porque el software de Microsoft no es Open Source. .NET sólo puede correr en servidores distintos de Microsoft Internet Information Server si se usa Mono y los servidores Windows son más caros de alojar que los Linux. Además usar SQL Server (la elección natural para .NET) también es bastante más caro que elegir PostgreSQL o MySQL.

PYTHON

Python para la web es sinónimo de Django. Django apareció en 2005 como framework para la creación de sitios web de contenidos dinámicos, y, si eso es lo que hay que hacer, puede ser una excelente elección. Python es un lenguaje interpretado, no usa bytecodes como Java, y aunque existen versiones de Python que pueden correr sobre JVM yo no conozco a nadie que las use en producción. El tipado dinámico y los tipos de alto nivel hacen que el código Python sea más corto que Java. Como RoR, Django incluye un subsistema MVC (aunque algo ecléctico) y también mapeador objeto-relacional, un sistema extensible de plantillas basado en etiquetas, un despachador de URLs basado en expresiones regulares y middleware para cacheo, compresión de la salida, normalización de URLs, protección Cross-Site Request Forgery (CSRF) y soporte de sesiones. Lo normal es ejecutar Python con el mod_python de Apache 2. Aunque gracias al soporte WSGI se puede correr también sobre Lighttpd. En general es de esperar que el rendimiento de Python sea inferior al de Java y, por consiguiente, ello puede ser un obstáculo si se pretende llevar el tráfico hasta el límite. Algunos sitios web de alto tráfico como Instagram o Pinterest están desarrollados con Django, aunque ellos mismos han reconocido que tuvieron problemas con la escalabilidad debido a su vertiginoso crecimiento, no sólo por Python sino también en gran parte por llegar al límite de las tecnologías de almacenamiento de datos. My experiencia personal con Python/Django es que es muy bueno como como “lenguaje pegamento”, es como un “Perl con esteroides”, pero no es tan bueno como lenguaje para una plataforma, porque permite programar muy sucio y porque es fácil embarullar la capa del Controlador en el MVC de Django. Python es relativamente sencillo de aprender, y a la mayoría de los programadores profesionales les gusta cuando llegan a conocerlo, pero, debido a que existe menos demanda de programadores Python que de Java, .NET o PHP estos son más difíciles de encontrar.


RUBY ON RAILS

Ningún framework de los anteriores levanta tantas pasiones a favor y en contra que Ruby On Rails. Como Django, RoR es un framework que se pensó desde el principio para el diseño de aplicaciones web. Es un framework de pila completa, lo que significa que trata de integrarlo todo desde la base de datos hasta el código que corre en el navegador cliente. RoR incorpora de serie el paradigma MVC, el mapeo objeto-relacional, infraestructura para crear recursos REST y otras funcionalidades propias del desarrollo web como un detector de inyección de JavaScript y SQL. RoR también incorpora JQuery, y se pueden conseguir herramientas de terceros como Aptana para el desarrollo de las páginas HTML5. Los críticos de RoR argumentan que es lento y que no escala bien en webfarms. Es cierto que las sesiones sólo se pueden compartir en Ruby a través de la base de datos. Pero es que yo soy partidario de que las aplicaciones no deben mantener nada en una sesión de lado servidor sino que se deben usar cookies para mantener las credenciales del usuario y luego cachear el resto. El sitio web más grande basado en RoR probablemente es Twitter. Otra crítica común es que, debido a la integración transparente entre el lenguage y la base de datos, no se puede usar Ruby fácilmente contra un modelo de datos que ya esté creado o siga unas normas de programación del estilo de sólo poder acceder a la información vía procedimientos almacenados, o, más bien, sí se puede, pero entonces se le mata la magia de ActiveRecord al lenguaje. Ruby es relativamente fácil de aprender, más o menos como PHP sólo que la documentación de Ruby no es tan extensa ni detallada. Hay muchos menos programadores de Ruby que de Java o de PHP. Sus defensores argumentan que los programadores de Ruby son pocos porque son todos unos gurús, mas creo que leyendo este artículo ya se sabe lo que opino de los gurús.

TECNOLOGÍAS FRONT-END

La compatibilidad entre navegadores web ha sido desde siempre una merienda de negros y con la llegada de HTML5 y CSS3 la situación no ha mejorado para nada. La oveja negra es Internet Explorer sobre todo las versiones anteriores a la 10. Es virtualmente imposible desarrollar una aplicación HTML5 cross-browser sin una librería de compatibilidad como Modernizr. CSS también tiene tantas sutiles diferencias que prácticamente nadie desarrolla sin un paraguas como YUI. Y para el desarrollo en JavaScript es totalmente común encontrar JQuery o Dojo. Existen decenas de otras librerías como Bootstrap, Backbone, CoffeeScript, Datejs, RequireJS, PhantomJS, etc. O frameworks como Ext.js. Incluso se puede desarrollar en Java Swing y convertirlo a JavaScript con GWT o, aún más exótico, desarrollar en Dart y compilarlo a JavaScript. Una herramienta muy útil para componer CSS es LESS aunque hay que precompilar siempre los scripts puesto que a Rhino le puede costar 2 segundos compilar un archivo .less de ~30Kb lo cual es un tiempo muy significativo en la carga de una página web.


  
  


CONCLUSIÓNES

         Ninguna plataforma es óptima para todas las necesidades. Para concluir con algunas reglas sencillas, mi propuesta es la siguiente:

         Si tienes que desarrollar un sitio web para una multinacional, o hacer integraciones complejas con otras plataformas o realmente vas a crecer mucho tanto en tráfico como en número de desarrolladores, entonces elige Java sobre PostgreSQL.

         Si quieres tener presencia online eficaz y asequible, incluso e-commerce, pero tu website no es el factor crítico exclusivo de tu negocio, entonces elige LAMP.

         Si tienes que desarrollar una intranet o un sitio web corporativo a sabiendas de que los usuarios tendrán Internet Explorer y tecnologías Microsoft entonces elige .NET sobre SQL Server

         Si necesitas una web con contenidos dinámicos mantenida por un equipo compacto y eficiente de programadores entonces elige Django sobre PostgreSQL o RoR sobre MySQL.


REFERENCIAS

         MONTORO, Sergio, “pastilla roja” octubre 2013.

         GONZÁLEZ,  Javier A, Presidente Rama IEEE Universidad Pedagógica y Tecnológica de Colombia
2002
http://aulavirtual.tecnologicocomfenalcovirtual.edu.co/aulavirtual/mod/resource/view.php?id=31881



ELABORADO POR:
Shirley Sanchez
Ingeniera de Sistemas (formación)
FUNDACION UNIVERSITARIA SAN MARTIN