Categoría: Programación

27 Dic 2016

Construye Elegantes Componentes Rails Con Plain Old Ruby Objects

Extrae Objetos Query De Los Controladores

¿Qué es un Objeto Query?

Un objeto Query es un PORO, el cual representa una base de datos de consulta. Puede ser reusada en diferentes lugares de la aplicación, mientras que, al mismo tiempo, esconde la lógica de consulta. También provee una buena unidad aislada para pruebas.

Deberías extraer consultas SQL/NoSQL complejas hacia sus propias clases.

Cada objeto Query es responsable de regresar un set de resultados basado en las reglas de criterio/negocio.

En este ejemplo, no tenemos ninguna consulta (query) compleja, así que usar objeto Query no sería eficiente. Sin embargo, con el fin de demostrar, extraeremos la consulta en Report::GenerateWeekly#call y crearemos generate_entries_query.rb:

Y en Report::GenerateWeekly#call, reemplacemos:

 def call
   @user.entries.group_by(&:week).map do |week, entries|
     WeeklyReport.new(
      ...
     )
   end
 end

con:

 def call
   weekly_grouped_entries = GroupEntriesQuery.new(@user).call

   weekly_grouped_entries.map do |week, entries|
     WeeklyReport.new(
      ...
     )
   end
 end

El patrón de objeto query (consulta) ayuda a mantener la lógica de tu modelo estrictamente relacionada a un comportamiento de clase, mientras que mantiene tus controladores flacas. Debido a que no son más que plain old Ruby classes, los objetos query no necesitan heredar de ActiveRecord::Base, y deberían ser responsables por nada más que la ejecución de consultas.

Extrae Crear Entrada A Un Objeto de Servicio

Ahora, vamos a extraer la lógica de crear una nueva entrada a un nuevo objeto de servicio. Vamos a usar la convención y creemos CreateEntry:

Y ahora nuestro EntriesController#create es de la siguiente manera:

 def create
   begin
     CreateEntry.new(current_user, entry_params).call
     flash[:notice] = 'Entry was successfully created.'
   rescue Exception => e
     flash[:error] = e.message
   end

   redirect_to root_path
 end

Más Validaciones A Un Objeto De Forma

Ahora las cosas comienzan a ponerse más interesantes.

Recuerda que en nuestras directrices acordamos que queríamos que los modelos tuvieran asociaciones y constantes, pero nada más (ni validaciones ni llamados). Así que empecemos por remover los llamados y usa un objeto de Forma en su lugar.

Un objeto de Forma es un PORO (Plain Old Ruby Object). Toma el mando del controlador/objeto de servicio cuando necesite hablar con la base de datos.

¿Por qué usar objetos de Forma?

Cuando necesites refactorizar tu aplicación, siempre es buena idea tener en mente, la responsabilidad única principal (SRP).

SRP te ayuda a tomar mejores decisiones de diseño, en cuanto a la responsabilidad que debe tener una clase.

Tu modelo de mesa de base de datos (un modelo ActiveRecord en el contexto de Rails), por ejemplo, representa un record de la base de datos único en código, así que no hay razón para que esté preocupado con nada que haga tu usuario.

Aquí es donde entra el objeto de Forma.

Un objeto de Forma es responsable de representar una forma en tu aplicación. Así que cada campo de entrada puede ser tratado como un atributo en la clase. Puede validar que esos atributos cumplen algunas reglas de validación, y puede pasar la data “limpia” a donde debe ir (ej., tu modelo de base de datos o tal vez tu constructor de búsqueda de consultas).

¿Cuándo deberías usar un objeto de Forma?

  • Cuando quieras extraer las validaciones de los modelos Rails.
  • Cuando múltiples modelos pueden ser actualizados por una sola forma de entrega, deberías crear un objeto de Forma.

Esto te permite poner toda la lógica de forma (nombrar convenciones, validaciones, y otros) en un solo lugar.

¿Cómo crear un objeto de Forma?

  • Crea una clase simple Ruby.
  • Incluye ActiveModel::Model (en Rails 3, tienes que incluir Nombre, Conversión y Validación, en su lugar).
  • Empieza a usar tu nueva clase de forma, como si fuera un modelo regular de ActiveRecord, donde la mayor diferencia es que no puedes continuar con la data almacenada en este objeto.

Por favor, ten en cuenta que puedes usar la gem reform, pero siguiendo con PORO, crearemos entry_form.rb lo cual se ve así:

Y modificaremos CreateEntry para comenzar a usar el objeto de Formato EntryForm:

     class CreateEntry
      
      ......
      ......

       def call
         @entry_form = ::EntryForm.new(@params)

         if @entry_form.valid?
            ....
         else
            ....
         end
       end
     end

Nota: Algunos de ustedes dirán que no hay necesidad de acceder al objeto de Forma desde el objeto de Servicio y que podemos llamar al objeto de Forma directamente desde el controlador, lo cual es un argumento válido. Sin embargo, preferiría tener un flujo claro, por eso siempre llamo al objeto de Forma desde objeto de Servicio.

Mueve los Llamados al Objeto de Servicio.

Como acordamos anteriormente, no queremos que nuestros modelos contengan validaciones y llamados. Extrajimos las validaciones usando objetos de Forma. Pero todavía estamos usando algunos llamados (after_create en modelo Entry compare_speed_and_notify_user).

¿Por qué queremos remover los llamados de los modelos?

Desarrolladores Rails usualmente comienzan a notar un dolor con los llamados, durante las pruebas. Si no estás haciendo pruebas con tus modelos ActiveRecord, comenzarás a notar el dolor después, mientras crece tu aplicación y mientras se necesite más lógica para llamar o evitar los llamados.

después_* los llamados son usados primordialmente en relación a guardar o continuar con el objeto.

Una vez que el objeto es guardado, el propósito (ej. responsabilidad) del objeto ha sido cumplido. Así que, si todavía vemos llamados siendo invocados, después de que el objeto ha sido guardado, estos probablemente son llamados que buscan salir del área de responsabilidad de objetos, y ahí es cuando encontramos problemas.

En nuestro caso, estamos enviando un SMS al usuario, lo que no está relacionado con el dominio de Entrada.

Una manera simple de resolver el problema es, mover el llamado al objeto de servicio relacionado. Después de todo, enviar un SMS para el usuario correspondiente está relacionado al Objeto de Servicio CreateEntry y no al modelo Entrada, como tal.

Al hacer esto, ya no tenemos que apagar, el método compare_speed_and_notify_user en nuestras pruebas. Hemos hecho que esto sea un asunto sencillo, el crear una entrada sin que sea necesario enviar un SMS y estamos siguiendo un buen diseño de Objeto Orientado, al asegurarnos de que nuestras clases tengan una responsabilidad única (SRP).

Así que ahora CreateEntry es algo similar a esto:

Usa Decoradores En Vez De Helpers

Aunque podemos fácilmente usar la colección Draper de modelos de vista y decoradores, me quedo con PORO, por este artículo, como lo he estado haciendo hasta ahora.

Lo que necesito es una clase que llame métodos al objeto decorado.

Puedo usar method_missing para implementar eso, pero usaré la biblioteca estándar de Ruby, SimpleDelegator. El siguiente código muestra cómo usar SimpleDelegator para implementar nuestro decorador base:

   % app/decorators/base_decorator.rb
   require 'delegate'

   class BaseDecorator < SimpleDelegator
     def initialize(base, view_context)
       super(base)
       @object = base
       @view_context = view_context
     end

     private

     def self.decorates(name)
       define_method(name) do
         @object
       end
     end

     def _h
       @view_context
     end
   end

¿Por qué el método _h?

Este método actúa como un proxy para contexto de vista. Por defecto, el contexto de vista es una instancia de una clase vista, siendo ésta ActionView::Base. Puedes acceder a los helpers de vistas de la siguiente manera:

   _h.content_tag :div, 'my-div', class: 'my-class'

Para hacerlo más conveniente agregamos un método decorado a ApplicationHelper:

   module ApplicationHelper

     # .....

     def decorate(object, klass = nil)
       klass ||= "#{object.class}Decorator".constantize
       decorator = klass.new(object, self)
       yield decorator if block_given?
       decorator
     end

     # .....

   end

Ahora, podemos mover los helpers EntriesHelper a los decoradores:

   # app/decorators/entry_decorator.rb
   class EntryDecorator < BaseDecorator
     decorates :entry

     def readable_time_period
       mins = entry.time_period
       return Time.at(60 * mins).utc.strftime('%M <small>Mins</small>').html_safe if mins < 60
       Time.at(60 * mins).utc.strftime('%H <small>Hour</small> %M <small>Mins</small>').html_safe
     end

     def readable_speed
       "#{sprintf('%0.2f', entry.speed)} <small>Km/H</small>".html_safe
     end
   end

Y podemos usar readable_time_period y readable_speed de la siguiente forma:

   # app/views/entries/_entry.html.erb
   -  <td><%= readable_speed(entry) %> </td>
   +  <td><%= decorate(entry).readable_speed %> </td>
   -  <td><%= readable_time_period(entry) %></td>
   +  <td><%= decorate(entry).readable_time_period %></td>

Estructura Después De Refactorizar

Terminamos con más archivos, pero eso no es necesariamente algo malo (y recuerda esto, desde el comienzo, estábamos conscientes de que este ejemplo era con fines demostrativos y no era necesariamente un buen caso de uso para refactorización):

   app
   ├── assets
   │   └── ...
   ├── controllers
   │   ├── application_controller.rb
   │   ├── entries_controller.rb
   │   └── statistics_controller.rb
   ├── decorators
   │   ├── base_decorator.rb
   │   └── entry_decorator.rb
   ├── forms
   │   └── entry_form.rb
   ├── helpers
   │   └── application_helper.rb
   ├── mailers
   ├── models
   │   ├── entry.rb
   │   ├── entry_status.rb
   │   └── user.rb
   ├── queries
   │   └── group_entries_query.rb
   ├── services
   │   ├── create_entry.rb
   │   └── report
   │       └── generate_weekly.rb
   └── views
       ├── devise
       │   └── ..
       ├── entries
       │   ├── _entry.html.erb
       │   ├── _form.html.erb
       │   └── index.html.erb
       ├── layouts
       │   └── application.html.erb
       └── statistics
           └── index.html.erb

Conclusión

Aunque nos enfocamos en Rails en este blog post, RoR (Ruby on Rails) no es una dependencia de los objetos de servicio ni de otros POROs. Puedes usar este enfoque con cualquier framework web, móvil o aplicación de consola.

Al usar MVC como arquitectura, todo se mantiene junto y te hace ir más lento porque la mayoría de los cambios tienen un impacto en otras partes de la aplicación. También te obliga a pensar donde poner algunas lógicas de negocio – ¿debería ir en el modelo, el controlador o la vista?

Al usar un simple PORO, hemos movido la lógica de negocio a los modelos o servicios, que no heredan de ActiveRecord, lo cual ya es una ganancia, sin mencionar que tenemos un código más claro, lo cual apoya SRP y pruebas de unidades más rápidas.

Una arquitectura limpia intenta poner las casillas de uso en el centro/parte superior de tu estructura para que veas fácilmente lo que hace tu aplicación. También facilita la adopción de cambios, porque es más modular y aislado. Espero haber demostrado como al Plain Old Ruby Objects y más abstracciones, separa preocupaciones, simplifica pruebas y ayuda a producir código limpio y fácil de mantener.

 

El artículo original lo pueden encontrar en Totpal.

28 Nov 2016

Guía de Desarrolladores de Android para el Patrón de Navegación de Fragmentos

BY BECZE SZABOLCS – FREELANCE SOFTWARE ENGINEER @ TOPTAL(TRANSLATED BY MARISELA ORDAZ)

 

Con el pasar de los años he visto diferentes implementaciones del patrón de navegación de Android. Algunas aplicaciones usaban solo Actividades, mientras que otras Actividades se mezclaban con Fragmentos y/o con Vistas Personalizadas (Custom Views).

Una de mis implementaciones de patrón de fragmentos favorita está basada en la filosofía “Una-Actividad-Múltiples-Fragmentos” (“One-Activity-Multiple-Fragments”), o sencillamente el Patrón de Navegación de Fragmentos (Fragment Navigation Pattern), en donde cada pantalla en la aplicación es un Fragmento de pantalla completa y todos o la mayoría de estos fragmentos se encuentran dentro de una Actividad.

Este enfoque no solo simplifica como se implementa la navegación, sino que también tiene un mejor rendimiento y por ende le ofrece una mejor experiencia de usuario.

Android Developer’s Guide to Fragment Navigation Pattern

En este artículo exploraremos algunas implementaciones de patrón de navegación comunes en Android, luego introduciremos el patrón de navegación basado en Fragmentos. Una aplicación de muestra que implementa este patrón puede ser encontrada en GitHub.

Un Mundo de Actividades

Una aplicación de Android típica que utiliza solamente actividades está organizada en una estructura de tipo Árbol (precisamente en una grafo dirigido) donde la actividad principal en iniciada por el launcher. Mientras navegas por la aplicación hay un “back stack” de la actividad que se mantiene gracias el Sistema Operativo.

Un ejemplo sencillo se muestra en el siguiente diagrama:

Actividad A1 es el punto de entrada de nuestra aplicación (por ejemplo, representa una pantalla inicial o un menú principal) y desde ese punto el usuario puede navegar hacia A2 o A3. Cuando necesites comunicarte entre actividades puedes utilizar startActivityForResult() o podrías compartir un objeto lógico de negocios globalmente accesible entre ambas actividades.

Cuando necesites agregar una nueva Actividad deberás seguir los siguientes pasos:

  • Define la nueva actividad
  • Regístrala en AndroidManifest.xml
  • Ábrela con un startActivity() de otra actividad

Aunque este diagrama de navegación es un enfoque bastante simplificado; pero puede volverse muy complicado cuando necesitas manipular de back stack o cuando tienes que re-utilizar la misma actividad varias veces, por ejemplo cuando te gustaría que el usuario navegará a través de diferentes pantallas de tutoriales pero cada pantalla utiliza la misma actividad como base.

Por suerte tenemos herramientas para estos casos llamadas tareas y algunas guías para una navegación de back stack apropiada.

Luego, con API nivel 11 llegaron los fragmentos…

Un Mundo de Fragmentos

“Android introduce los fragmentos en Android 3.0 (nivel de API 11), principalmente para admitir diseños de IU más dinámicos y flexibles en pantallas grandes, como las de las tablets. Como la pantalla de una tablet es mucho más grande que la de un teléfono, hay más espacio para combinar e intercambiar componentes de la IU. Los fragmentos admiten esos diseños sin la necesidad de que administres cambios complejos en la jerarquía de vistas. Al dividir el diseño de una actividad en fragmentos, puedes modificar el aspecto de la actividad durante el tiempo de ejecución y conservar esos cambios en una pila de actividades administrada por la actividad.” – cita de la Guía de Google API para Fragmentos.

Este Nuevo juguete permitió a los desarrolladores crear una Interfaz de Usuario multi-paneles y poder re-usar los componentes en otras actividades. Algunos desarrolladores adoran esto mientras que otros no tanto. Es un debate popular el hecho de usar o no Fragmentos, pero yo creo que todos estarán de acuerdo en que los fragmentos trajeron una complejidad adicional y los desarrolladores tienen que entenderlos apropiadamente para poder usarlos.

La Pesadilla del Fragmento de Pantalla Completa en Android

Comencé a ver más y más ejemplos donde los fragmentos no solo representaban una parte de la pantalla, sino que toda la pantalla era un fragmento dentro de una actividad. Hubo una vez que vi un diseño en el que cada actividad tenía exactamente un fragmento de pantalla completa y nada más, y la única razón por la que esas actividades existían era para almacenar los fragmentos. Aparte del fallo en el diseño, hay otro problema con este enfoque. Mira un momento el diagram a continuación:

¿Cómo puede A1 comunicarse con F1? Lo que sucede es que A1 tiene total control sobre F1, debido a que creó F1. A1 podría pasar un paquete, por ejemplo, en la creación de F1 o puede invocar sus métodos públicos. ¿Cómo puede F1 comunicarse con A1? Bueno eso es más complicado, puede resolverse con un patrón de callback/observer donde A1 se suscribe a F1 y F1 notifica a A1.

¿Pero cómo se pueden comunicar A1 y A2? Como se explicó anteriormente, se podrían comunicar vía startActivityForResult().

Y ahora la verdadera pregunta: ¿Cómo se pueden comunicar F1 y F2? Aún en este caso podemos tener un componente lógico de negocios el cual es accesible globalmente, y puede ser utilizado para pasar datos. Pero ese componente no siempre equivale a un diseño elegante. ¿Qué pasaría si F2 necesita pasarle información a F1 de forma más directa? En tal caso, con un patrón callback F2 puede notificar a A2, luego A2 termina con un resultado y ese resultado puede ser almacenado por A1 quien puede notificar a F1.

Este enfoque requiere de mucho código boilerplate y se convierte rápidamente en una fuente de bugs, dolor y rabia.

¿Y si pudiéramos deshacernos de todas las actividades y quedarnos sólo con una de ellas la cual mantendrá el resto de los fragmentos?

Patrón de Navegación de Fragmentos

Con el pasar del tiempo comencé a utilizar el patrón de “Una-Actividad-Múltiples-Fragmentos” en la mayoría de mis aplicaciones y aún lo sigo usando. Existen muchas discusiones por ahí sobre este enfoque o filosofía, por ejemplo aquí y aquí. Lo que me perdí fue un ejemplo en concreto que puedo ver y probar por mí mismo.

Miremos el siguiente diagrama un momento:

Ahora tenemos solo una actividad contenedora y tenemos fragmentos múltiples que de nuevo se encuentran en una estructura de tipo Árbol. La navegación entre ellos se maneja por el FragmentManager, este tiene su back stack.

Te podrás cuenta que ahora no tenemos el startActivityForResult() pero podemos implementar el patrón callback/observer. Ahora veamos algunos pros y contras de este enfoque:

Pros:

1. Más limpio y mantenible AndroidManifest.xml

Ahora que tenemos sólo una Actividad, ya no tenemos que actualizar el manifiesto cada vez que agregamos una nueva pantalla. A diferencia de las actividades, no tenemos que declarar los fragmentos.

Esto podría parecer una cosa pequeña, pero para aplicaciones más grandes con más de 50 actividades esto podría mejorar significativamente la legibilidad del AndroidManifest.xml file.

Observa el archivo del manifiesto de la aplicación ejemplo, el cual tiene varias pantallas. El archivo de manifiesto se mantiene súper sencillo.

<?xml version="1.0" encoding="utf-8"?>
  package="com.exarlabs.android.fragmentnavigationdemo.ui" >
  <application android:name= ".FragmentNavigationDemoApplication"
android:allowBackup="true"
  	android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name="com.exarlabs.android.fragmentnavigationdemo.ui.MainActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

2. Gestión centralizada de la navegación

En mi código ejemplo, notarás que use NavigationManager el cual en mi caso, es inyectado a cada uno de los fragmentos. Éste gestor puede ser usado como un lugar central para el logging, gestión back stack entre otras, así que los comportamientos de navegación se desacoplan del resto de la lógica del negocio y no se esparcen en implementaciones de diferentes pantallas.

Imaginemos una situación en la que queremos iniciar una pantalla, donde el usuario puede seleccionar algunos ítems de una lista de personas. También te gustaría pasar algunos argumentos de filtrado, como edad, ocupación y género.

En caso de Actividades, escribirías:

Intent intent = new Intent();
intent.putExtra("age", 40);
intent.putExtra("occupation", "developer");
intent.putExtra("gender", "female");
startActivityForResult(intent, 100);

Luego, debes definir onActivityResult en algún lugar abajo y manejar el resultado.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}

Mi problema personal con éste acercamiento es que estos argumentos son “extras” y no son obligatorios, así que tengo que asegurarme que la actividad receptora maneje los diferentes casos, cuando falte un extra. Después cuando se realice una refactorización y cuando el extra, por ejemplo, de “edad” ya no sea necesario, entonces tengo que buscar por todo el código donde se inició esta actividad y asegurarme de que todos los extras sean correctos.

Además, ¿No sería mejor si el resultado (lista de personas) llegará como _List_ y no en una forma serial que luego debe ser deserializada?

En el caso de la navegación basada en fragmentos, todo es más directo. Todo lo que tienes que hacer es escribir un método en el NavigationManager llamado startPersonSelectorFragment() con los argumentos necesarios y con una implementación callback.

mNavigationManager.startPersonSelectorFragment(40, "developer", "female",
new PersonSelectorFragment.OnPersonSelectedListener() {
@Override
public boolean onPersonsSelected(List<Person> selection) {
[do something]
return false;
}
});

O con RetroLambda

mNavigationManager.startPersonSelectorFragment(40, "developer", "female", selection -> [do something]);

3. Mejores formas de comunicación entre pantallas

Entre actividades, solo podemos compartir un Paquete que contenga datos primitivos o serializados. Ahora con fragmentos podemos implementar un patrón callback donde, por ejemplo, F1 puede escuchar a F2 pasando objetos arbitrarios. Por favor, dale un vistazo a los ejemplos anteriores de implementación de callback, which returns back a _List_.

4. Crear Fragmentos es menos costoso que crear Actividades

Esto se vuelve obvio cuando usas un cajón que tiene por ejemplo 5 ítems de menú y en cada página el cajón debería ser mostrado nuevamente.

En caso de navegación de actividad pura, cada página debería inflarse e iniciar el cajón, pero por supuesto esto es caro.

En el diagrama que se muestra puedes ver varios fragmentos de raíz o root fragments (FR*) los cuales son los fragmentos de pantalla que pueden ser accedidos directamente desde el cajón, y también el cajón es solo accesible cuando se muestran estos fragmentos. Todo lo que se encuentra a la derecha de la línea marcada en el diagrama está ahí como ejemplo de un esquema de navegación arbitraria.

Ya que la actividad contenedora contiene el cajón, solo tenemos una instancia de cajón, por lo que cada paso de navegación donde el cajón debería ser visible no tienes que iniciarlo nuevamente. ¿Aún no estas convencido de cómo funciona todo? Dale un vistazo a mi aplicación de muestra donde se demuestra el uso de los cajones.

Contras

Mi gran temor siempre ha sido que si utilizo el patrón de navegación de fragmentos en un proyecto, entonces en algún momento encontraré algún problema desconocido que será difícil de resolver alrededor de la complejidad de los fragmentos, las bibliotecas de terceros y las diferentes versiones de Sistema Operativo. ¿Y si tuviera que refractar todo lo que he hecho hasta ahora?

De hecho, tendría que resolver los problemas con nested fragments, bibliotecas de terceros las cuales utilizan fragmentos como ShinobiControls, ViewPagers y FragmentStatePagerAdapters.

Admito que obtener la experiencia suficiente con fragmentos como para resolver estos problemas fue un largo proceso. Pero en cada caso el problema no era que la filosofía es mala, sino que no entendía fragmentos lo suficiente. Pero si entiendes fragmentos mejor que yo en aquel momento entonces no tendrás ningún problema.

El único contra que puedo mencionar ahora es que podemos encontrar problemas que no sean triviales de resolver, debido a que no hay una biblioteca madura que muestre todos los escenarios complejos de una aplicación compleja con navegación basada en fragmentos.

Conclusión

En este artículo, hemos visto una alternativa para implementar navegación en una aplicación Android . Se comparó el patrón con la filosofía de navegación tradicional que utiliza actividades y vimos algunas muy buenas razones por las que es ventajoso usar fragmentos en lugar del enfoque tradicional.

En caso de que no lo hayas revisado aún, chequea la aplicación demo en la implementación GitHub . No temas contribuir en la misma con buenos ejemplos que podrían demostrar de mejor forma su uso.

El artículo original lo pueden encontrar en Totpal.

21 Nov 2016

Guarda la Calma y Transiciona a un Nuevo Equipo de Desarrollo

Gestionando el traspaso

Ahora que has cubierto todas las bases, necesitas gestionar el traspaso de un equipo a otro. Estos son algunos consejos básicos para enfrentar tanto al equipo entrante como al de salida.

Make sure you properly manage the technical and personal aspects of your project handoff. Make your new team feel at home and don’t antagonize your old team.

Asegúrate de administrar adecuadamente los aspectos técnicos y personales de tu traspaso de proyecto. Permite que tu nuevo equipo se sienta como en casa.

Equipo Entrante

  • Establece expectativas – El nuevo equipo debe saber cuáles son tus objetivos más importantes para que puedan centrarse en la dirección correcta. La gestión de tus propias expectativas sobre lo que el nuevo equipo puede lograr de inmediato es igualmente importante.
  • Realiza visitas de control a menudo – No dejes al nuevo equipo a su suerte. Debes hacer visitas de control con frecuencia para asegurarte de que tienen todo lo que necesitan, y que no se sientan como que tienen que valerse por sí mismos. Trata de hacer esto sin llegar a hacer micro gestión. Asegúrate de que sepan que estás para apoyar y ayudar si lo necesitan, pero no pongas presión innecesaria sobre ellos.
  • Sé paciente – Se necesita tiempo para que los desarrolladores puedan adaptarse a una nueva base de códigos. Entiende que habrá un poco de tiempo de aprendizaje antes de que el nuevo equipo pueda igualar el ritmo del anterior.

Equipo Saliente

  • Recolecta todo el código en circulación – Asegúrate de que todo el código fuente se haya registrado en el repositorio principal, y que sabes el estado de lo que se ha desplegado y lo que no. El nuevo equipo tendrá que saber exactamente dónde retomar y empezar a trabajar. Yo mismo he experimentado una situación en la que seguí el trabajo de un equipo que había desplegado código sin ponerlo en el repositorio principal. Esto llevó a que se crearan bugs, la duplicación de trabajo y dolores de cabeza que podrían haberse evitado fácilmente si el equipo saliente hubiera dejado el código fuente en un estado coherente.
  • Actualiza el nivel de acceso – Si se separaron en buenos términos, es posible que desees mantenerlos con acceso a tu código y/o despliegue. Muchos equipos están dispuestos a ayudar durante la fase de transición, hasta que el nuevo equipo pueda asumir plenamente. Si no, considera cambiar o revocar el acceso para evitar cualquier problema accidental o conflictos con el nuevo equipo.
  • Dales las gracias por su trabajo – Las transiciones pueden ser agitadas. Mientras estás ocupado con el nuevo equipo, no te olvides de agradecer a tu equipo saliente por su contribución al proyecto.

Conclusión

Cualquier transición en la vida puede dar miedo, lo cual trae la incertidumbre de si funcionará o no, el miedo a lo desconocido, y así sucesivamente. La transición a un nuevo equipo de desarrollo no es diferente, pero puedes y debes tomar medidas para que sea más fácil. En la mayoría de los casos, sólo se requiere un poco de planificación a largo plazo.

Tener un mayor conocimiento técnico y no técnico de tu producto de software, el proceso de desarrollo, y todas las cosas que entraron en el proceso ayudará a que cualquier transición de un equipo a otro, sea tan tranquila y sin dolor como sea posible.

Lo mejor de todo: ¡Tu nuevo equipo te respetará y dará las gracias por estar preparado! Es probable que les ahorre tiempo y esfuerzo, lo que también significa que vas a ahorrar dinero. Además, cuanto más pronto el nuevo equipo se dé cuenta de la insistencia en un alto nivel profesional, mejor. Lo más probable es que continúen la implementación de estas prácticas una vez tomen el control del proyecto, por lo que la próxima transición será también sin problemas.

Así que vamos a revisar los puntos clave que deben preceder a cualquier transferencia de propiedad de tu producto de software:

  • Recoger o crear la mayor documentación posible acerca de tu aplicación, el entorno de desarrollo y el proceso de despliegue.
  • Conocer tu producto dentro y por fuera.
  • Mantener el control de todos los servicios y dependencias de terceros de tu aplicación, y tener los nombres de usuario y contraseñas para todo.
  • Esté preparado para darle a tu nuevo equipo acceso a todo lo que necesitan para empezar a funcionar.
  • Sé proactivo y no dejes nada al azar, o para el equipo de desarrollo de salida.

 

El artículo original lo pueden encontrar en Totpal.

15 Nov 2016

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

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 utilizaratexit.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.

El artículo original lo pueden encontrar en Totpal.

08 Nov 2016

Codificación de Datos: Una Guía UTF-8 para PHP y MySQL

El artículo original lo pueden encontrar en Totpal.

31 Oct 2016

Guía para Desarrolladores iOS: Desde Objective-C hasta Swift

Clases y Estructuras

A diferencia de Objective-C, Swift no requiere que crees documentos de interfaz e implementación por separado para clases y estructuras personalizadas. Mientras aprendes sobre Swift, aprenderás a definir una clase o estructura en un solo documento y la interfaz externa para esa clase o estructura se hace disponible automáticamente para el uso de otro código.

Definir Clases

Las definiciones de clase son my sencillas:

class Bottle
{
   var volume: Int = 1000
   
   func description() -> String
   {
       return "This bottle has \(volume) ml"
   }
}
let b = Bottle()
print(b.description())

Como puedes ver, declaración e implementación están en el mismo documento. Swift ya no utiliza un encabezado ni documentos de implementación. Agreguemos una etiqueta a nuestro ejemplo:

class Bottle
{
   var volume: Int = 1000
   var label:String
   
   func description() -> String
   {
       return "This bottle of \(label) has \(volume) ml"
   }
}

El compilador se quejará, ya que la etiqueta es una variable no-opcional, y ésta no mantendrá un valor cuando se ejemplifica una Bottle (Botella). Necesitamos agregar un inicializador:

class Bottle
{
   var volume: Int = 1000
   var label:String
   
   init(label:String)
   {
       self.label = label
   }

   func description() -> String
   {
       return "This bottle of \(label) has \(volume) ml"
   }
}

O podríamos usar tipo Opcional para una propiedad, el cual no necesita ser inicializado. En el siguiente ejemplo convertimos en volumen un Número entero Opcional:

class Bottle
{
   var volume: Int?
   var label:String
   
   init(label:String)
   {
       self.label = label
   }

   func description() -> String
   {
        if self.volume != nil
        {   
               return "This bottle of \(label) has \(volume!) ml"
           }
           else
           {
               return "A bootle of \(label)"
           }
   }
}

Estructuras

El lenguaje Swift también tiene structs, pero son mucho más flexibles que en Objective-C. El siguiente tutorial de código define un struct:

struct Seat
{
    var row: Int
    var letter:String
    
    init (row: Int, letter:String)
    {
        self.row = row
        self.letter = letter
    }
    
    func description() -> String
    {
        return "\(row)-\(letter)"
    }
}

Como las clases en Swift, las estructuras pueden tener métodos, propiedades, inicializadores y se ajustan a los protocolos. La diferencia principal entre clases y estructuras es que las clases se pasan por referencia, mientras que las estructuras lo hacen por valor.

Este ejemplo demuestra el pasar las clases por referencia:

let b = Bottle()
print(b.description())    // "b" bottle has 1000 ml

var b2 = b
b.volume = 750
print(b2.description())    // "b" and "b2" bottles have 750 ml

Si intentamos hacer algo similar con struct, notarás que las variables se pasan con valores:

var s1 = Seat(row: 14, letter:"A")
var s2 = s1
s1.letter = "B"
print(s1.description())    // 14-B
print(s2.description())    // 14-A

¿Cuándo debemos usar struct y cuándo usamos class? Al igual que en Objective-C y C, usa structs cuando necesites agrupar algunos valores y espera que sean copiados en vez de referenciados o colores RGB.

La instancia de una clase es conocida tradicionalmente como un objeto. Sin embargo, las clases y estructuras Swift son mucho más cercanas en funcionalidad que en otros lenguajes y se puede aplicar mucha funcionalidad a instancias de tipo estructura o clase. Por esto, el término más general utilizado en referencia Swift es instancia, el cual se aplica a cualquiera de estos dos.

Aprende lo básico sobre las clases y estructuras Swift aquí.

Propiedades

Como vimos anteriormente, las propiedades en Swift se declaran con la palabra clave var dentro de la definición de una clase o estructura. También podemos declarar con la instrucción let.

struct FixedPointNumber
{
    var digits: Int
    let decimals: Int
}

var n = FixedPointNumber(digits: 12345, decimals: 2)
n.digits = 4567    // ok
n.decimals = 3     // error, decimals is a constant

También ten en cuenta que las propiedades de la clase son fuertemente referenciadas, a menos que uses el prefijo weak como palabra clave. Sin embargo, hay algunas sutilezas con propiedades no-opcionales de weak, así que lee el capítulo Contabilidad de referencia automática en la Guía Swift de Apple..

Propiedades Calculadas

Las propiedades calculadas no almacenan un valor. Por el contrario, proporcionan un getter y un setter opcional para recuperar y establecer otras propiedades y valores indirectamente.

El siguiente código proporciona un ejemplo de un valor calculado sign:

enum Sign
{
    case Positive
    case Negative
}

struct SomeNumber
{
    var number:Int
    var sign:Sign
    {
        get
        {
            if number < 0
            {
                return Sign.Negative
            }
            else
            {
                return Sign.Positive
            }
        }
        
        set (newSign)
        {
            if (newSign == Sign.Negative)
            {
                self.number = -abs(self.number)
            }
            else
            {
                self.number = abs(self.number)
            }
        }
    }
}

También podemos definir propiedades de solo lectura con solo implementar un getter:

struct SomeNumber
{
    var number:Int
    var isEven:Bool
    {
        get
        {
            return number % 2 == 0
        }
    }
}

En Objective-C, las propiedades usualmente se respaldan con una variable de instancia, declarada explícitamente o creada automáticamente por el compilador. Por otra parte, en Swift, una propiedad no tiene una variable de instancia correspondiente. Es decir, no se puede acceder directamente al almacén de respaldo de una propiedad. Supón que tenemos esto en Objective-C:

// .h
@interface OnlyInitialString : NSObject

@property(strong) NSString *string;

@end

// .m

@implementation OnlyInitialString

- (void)setString:(NSString *newString)
{
    if (newString.length > 0)
    {
        _string = [newString substringToIndex:1];
    }
    else
    {
        _string = @"";
    }
}

@end

Ya que en Swift las propiedades calculadas no tienen un almacén de respaldo, necesitamos algo como esto:

class OnlyInitialString
{
    var initial:String = ""
    var string:String
    {
        set (newString)
        {
            if countElements(newString) > 0
            {
                self.initial = newString.substringToIndex(advance(newString.startIndex, 1))
            }
            else
            {
                self.initial = ""
            }
        }
        get
        {
            return self.initial
        }
    }
}

Las propiedades se explican con más detalle aquí

 

Continuará

Hay muchas cosas más importantes y nuevas que aprender en Swift como programación genérica, interacción con las bibliotecas Objective-C, cierres, optional chaining y sobrecarga de operadores. Un solo tutorial no puede describir completamente un nuevo lenguaje, pero no tengo dudas de que se escribirá mucho más sobre programación Swift. Sin embargo, creo que esta lectura rápida ayudará a muchos desarrolladores Objective-C, quienes no han encontrado el tiempo, ni detalles de aprendizaje sobre el lenguaje Swift, ponte en marcha y deja que el pájaro Swift te lleve a nuevas alturas.

 

El artículo original lo pueden encontrar en Totpal.

 

12 Oct 2016

Un Tutorial Paso-a-Paso para tu Primera Aplicación AngularJS

Cargando Datos del Servidor

Como ya sabemos cómo mostrar los datos de nuestro controlador en nuestra vista, es momento de traer datos en vivo desde un servidor RESTful.

Para facilitar la comunicación con los servidores HTTP, AngularJS proporciona los servicios $http y $resource. El primero es una capa en la parte superior de XMLHttpRequest o JSONP, mientras que el último proporciona un mayor nivel de abstracción. Vamos a utilizar $http.

Para abstraer nuestras llamadas a la API del servidor desde el controlador vamos a crear nuestro propio servicio personalizado, el cual va a capturar los datos y actuará como una envoltura alrededor de $http al añadirlo a nuestro services.js:

angular.module('F1FeederApp.services', []).
  factory('ergastAPIservice', function($http) {

    var ergastAPI = {};

    ergastAPI.getDrivers = function() {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    return ergastAPI;
  });

Con las dos primeras líneas, creamos un nuevo módulo (F1FeederApp.services) y registramos un servicio dentro de ese módulo (F1FeederApp.services). Nótese que pasamos $http como parámetro a ese servicio. Esto le dice al motor de inyección de dependenciade Angular, que nuestro nuevo servicio requiere (o depende) del servicio $http.

De una manera similar, tenemos que decirle a Angular que incluya nuestro nuevo módulo en nuestra aplicación. Vamos a registrarlo con app.js, reemplazando nuestro código existente con:

angular.module('F1FeederApp', [
  'F1FeederApp.controllers',
  'F1FeederApp.services'
]);

Ahora, lo único que tenemos que hacer es ajustar nuestra controller.js un poco, integrar ergastAPIservicecomo una dependencia, y estaremos listos para continuar:

angular.module('F1FeederApp.controllers', []).
  controller('driversController', function($scope, ergastAPIservice) {
    $scope.nameFilter = null;
    $scope.driversList = [];

    ergastAPIservice.getDrivers().success(function (response) {
        //Dig into the responde to get the relevant data
        $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
    });
  });

Ahora, recarga la aplicación y revisa el resultado. Observa que no hicimos ningún cambio en nuestra plantilla, pero añadimos una variable nameFilter a nuestro alcance. Vamos a poner esta variable en uso.

Filtros

¡Estupendo! Tenemos un controlador funcional. Pero sólo muestra una lista de conductores. Vamos a añadir algunas funciones mediante una simple entrada de búsqueda de texto, que filtrará la lista. Vamos a añadir la siguiente línea a nuestro index.html, justo debajo de la etiqueta <body>:

<input type="text" ng-model="nameFilter" placeholder="Search..."/>

Ahora estamos haciendo uso de la directriz ng-model. Esta directriz une nuestro campo de texto a la variable $scope.nameFilter y se asegura de que su valor esté siempre al día con el valor de entrada. Ahora, vamos a visitar index.html una vez más y hagamos un pequeño ajuste en la línea que contiene la directriz ng-repeat:

<tr ng-repeat="driver in driversList | filter: nameFilter">

Esta línea le dice a ng-repeat que, antes de dar salida a los datos, la matriz driversList debe ser filtrada por el valor almacenado en nameFilter.

En este punto, entran los datos bidireccionales binding: cada vez que un valor se introduce en el campo de búsqueda, Angular asegura inmediatamente que el $scope.nameFilter que asociamos a él se actualice con el nuevo valor. Dado que binding funciona en ambos sentidos, el momento en el que el valor nameFilter se actualiza, la segunda directriz asociada a la misma (es decir, ng-repeat) también recibe el nuevo valor y la vista se actualiza inmediatamente.

Actualiza la aplicación y observa la barra de búsqueda.

Observa que éste filtro buscará la palabra clave en todos los atributos del modelo, incluyendo los que no estamos usando. Digamos que sólo queremos filtrar Driver.givenName y Driver.familyName: En primer lugar, añadimos a driversController, justo por debajo de la línea $scope.driversList =[];:

$scope.searchFilter = function (driver) {
    var keyword = new RegExp($scope.nameFilter, 'i');
    return !$scope.nameFilter || keyword.test(driver.Driver.givenName) || keyword.test(driver.Driver.familyName);
};

Ahora, de vuelta a index.html, actualizamos la línea que contiene la directriz ng-repeat:

<tr ng-repeat="driver in driversList | filter: searchFilter">

Actualiza la aplicación una vez más y ahora tenemos una búsqueda por nombre.

Rutas

Nuestro próximo objetivo es crear una página de datos del conductor, la cual nos permitirá hacer clic en cada conductor y ver los detalles de su carrera.

En primer lugar, vamos a incluir el servicio $routeProvider (en app.js) lo que nos ayudará a lidiar con estas variadas rutas de aplicación. A continuación, añadiremos dos de estas rutas: una para la tabla del campeonato y otro para los datos del conductor. Aquí está nuestra nueva app.js:

angular.module('F1FeederApp', [
  'F1FeederApp.services',
  'F1FeederApp.controllers',
  'ngRoute'
]).
config(['$routeProvider', function($routeProvider) {
  $routeProvider.
	when("/drivers", {templateUrl: "partials/drivers.html", controller: "driversController"}).
	when("/drivers/:id", {templateUrl: "partials/driver.html", controller: "driverController"}).
	otherwise({redirectTo: '/drivers'});
}]);

Con éste cambio, la navegación hacia http://domain/#/drivers cargará el driversController y buscará la vista parcial que se va a renderizar en partials/drivers.html. ¡Pero espera! No tenemos ninguna vista parcial todavía, ¿verdad? Vamos a tener que crearlas también.

Vistas Parciales

AngularJS te permitirá unir tus rutas a los controladores y vistas específicas.

Pero primero, tenemos que decirle a Angular dónde renderizar estas vistas parciales. Para ello, usaremos la directriz ng-view, modificando nuestra index.html para reflejar lo siguiente:

<!DOCTYPE html>
<html>
<head>
  <title>F-1 Feeder</title>
</head>

<body ng-app="F1FeederApp">
  <ng-view></ng-view>
  <script src="bower_components/angular/angular.js"></script>
  <script src="bower_components/angular-route/angular-route.js"></script>
  <script src="js/app.js"></script>
  <script src="js/services.js"></script>
  <script src="js/controllers.js"></script>
</body>
</html>

Ahora, cada vez que naveguemos a través de nuestras rutas de aplicaciones, Angular cargará la vista asociada y la renderizará en lugar de la etiqueta <ng-view>. Lo único que tenemos que hacer es crear un archivo con el nombre partials/drivers.html, y poner nuestra tabla de campeonato HTML allí. También vamos a utilizar esta oportunidad para vincular el nombre del conductor a nuestra ruta de los detalles del conductor:

<input type="text" ng-model="nameFilter" placeholder="Search..."/>
<table>
<thead>
  <tr><th colspan="4">Drivers Championship Standings</th></tr>
</thead>
<tbody>
  <tr ng-repeat="driver in driversList | filter: searchFilter">
	<td>{{$index + 1}}</td>
	<td>
  	<img src="img/flags/{{driver.Driver.nationality}}.png" />
   	<a href="#/drivers/{{driver.Driver.driverId}}">
   	    	{{driver.Driver.givenName}}&nbsp;{{driver.Driver.familyName}}
   	  </a>
   	</td>
   <td>{{driver.Constructors[0].name}}</td>
   <td>{{driver.points}}</td>
  </tr>
</tbody>
</table>

Por último, vamos a decidir lo que queremos mostrar en la página de detalles. ¿Qué tal un resumen de todos los hechos relevantes sobre el conductor (por ejemplo, fecha de nacimiento, nacionalidad), junto con una tabla que contiene sus resultados recientes? Para hacer eso, añadimos a services.js, lo siguiente:

angular.module('F1FeederApp.services', [])
  .factory('ergastAPIservice', function($http) {

    var ergastAPI = {};

    ergastAPI.getDrivers = function() {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    ergastAPI.getDriverDetails = function(id) {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK'
      });
    }

    ergastAPI.getDriverRaces = function(id) {
      return $http({
        method: 'JSONP', 
        url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK'
      });
    }

    return ergastAPI;
  });

Esta vez, proporcionamos la identificación del conductor al servicio para que podamos recuperar la información relevante de un conductor específico. Ahora, modificamos controllers.js:

angular.module('F1FeederApp.controllers', []).

  /* Drivers controller */
  controller('driversController', function($scope, ergastAPIservice) {
    $scope.nameFilter = null;
    $scope.driversList = [];
    $scope.searchFilter = function (driver) {
        var re = new RegExp($scope.nameFilter, 'i');
        return !$scope.nameFilter || re.test(driver.Driver.givenName) || re.test(driver.Driver.familyName);
    };

    ergastAPIservice.getDrivers().success(function (response) {
        //Digging into the response to get the relevant data
        $scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
    });
  }).

  /* Driver controller */
  controller('driverController', function($scope, $routeParams, ergastAPIservice) {
    $scope.id = $routeParams.id;
    $scope.races = [];
    $scope.driver = null;

    ergastAPIservice.getDriverDetails($scope.id).success(function (response) {
        $scope.driver = response.MRData.StandingsTable.StandingsLists[0].DriverStandings[0]; 
    });

    ergastAPIservice.getDriverRaces($scope.id).success(function (response) {
        $scope.races = response.MRData.RaceTable.Races; 
    }); 
  });

Lo importante a notar aquí es que solo inyectamos el servicio $routeParams en el controlador del conductor. Este servicio nos permitirá acceder a nuestros parámetros de URL (para el :id, en este caso) utilizando $routeParams.id.

Ahora que tenemos nuestros datos en el alcance, sólo necesitamos la vista parcial restante. Vamos a crear un archivo con el nombre partials/driver.html y agregamos:

<section id="main">
  <a href="./#/drivers"><- Back to drivers list</a>
  <nav id="secondary" class="main-nav">
    <div class="driver-picture">
      <div class="avatar">
        <img ng-show="driver" src="img/drivers/{{driver.Driver.driverId}}.png" />
        <img ng-show="driver" src="img/flags/{{driver.Driver.nationality}}.png" /><br/>
        {{driver.Driver.givenName}} {{driver.Driver.familyName}}
      </div>
    </div>
    <div class="driver-status">
      Country: {{driver.Driver.nationality}}   <br/>
      Team: {{driver.Constructors[0].name}}<br/>
      Birth: {{driver.Driver.dateOfBirth}}<br/>
      <a href="{{driver.Driver.url}}" target="_blank">Biography</a>
    </div>
  </nav>

  <div class="main-content">
    <table class="result-table">
      <thead>
        <tr><th colspan="5">Formula 1 2013 Results</th></tr>
      </thead>
      <tbody>
        <tr>
          <td>Round</td> <td>Grand Prix</td> <td>Team</td> <td>Grid</td> <td>Race</td>
        </tr>
        <tr ng-repeat="race in races">
          <td>{{race.round}}</td>
          <td><img  src="img/flags/{{race.Circuit.Location.country}}.png" />{{race.raceName}}</td>
          <td>{{race.Results[0].Constructor.name}}</td>
          <td>{{race.Results[0].grid}}</td>
          <td>{{race.Results[0].position}}</td>
        </tr>
      </tbody>
    </table>
  </div>

</section>

Observa que ahora estamos dándole buen uso a la directriz ng-show. Esta directriz sólo mostrará el elemento HTML si la expresión proporcionada es true (es decir, ni false, ni null). En este caso, el avatar sólo aparecerá una vez que el objeto conductor ha sido cargado en el alcance, por el controlador.

Últimos Toques

Añade un montón de CSS y renderiza tu página. Deberías terminar con algo como esto:

Ahora estás listo para iniciar tu aplicación y asegúrate de que ambas rutas están funcionando como deseas. También puedes añadir un menú estático a index.html, para mejorar las capacidades de navegación del usuario. Las posibilidades son infinitas.

EDITADO (mayo de 2014): He recibido muchas peticiones para una versión descargable del código que construimos en este tutorial. Por lo tanto, he decidido hacerlo disponible aquí (despojado de cualquier CSS). Sin embargo, la verdad es que no recomiendo descargarlo, ya que ésta guía contiene cada paso que necesitas para generar la misma aplicación con tus propias manos, que será un ejercicio de aprendizaje mucho más útil y eficaz.

Conclusión

En este punto del tutorial, hemos cubierto todo lo que necesitarías para escribir una aplicación sencilla (como un informador de Fórmula 1). Cada una de las páginas restantes en el demo en vivo (por ejemplo, tabla del campeonato de constructores, detalles del equipo, calendario) comparten la misma estructura y conceptos básicos que hemos revisado.

Por último, ten en cuenta que Angular es un marco muy potente y que apenas hemos tocado la superficie, en términos de todo lo que tiene que ofrecer. En la parte 2 de éste tutorial, vamos a dar ejemplos de por qué Angular se destaca entre sus semejantes en marcos MVC front-end: capacidad de prueba. Vamos a revisar el proceso de escribir y ejecutar pruebas unitarias con Karma, lograr la integración continua con Yeomen, Grunt, y Bower y otros puntos fuertes de éste fantástico marco front-end.

 

El artículo original lo pueden encontrar en Totpal.

26 Sep 2016

Diseñando un Mejor Portal Web: Los Fundamentos Primero

BY ALEX GUREVICH – FREELANCE DESIGNER @ TOPTAL

 

¿Cuántas veces has terminado un proyecto, sólo para mirar atrás y decir: “¡Si tan sólo pudiera regresar en el tiempo y hacerlo todo de nuevo con lo que sé ahora!?” Al menos yo lo he pensado.

A todos nos pasa; estamos emocionados por un nuevo proyecto. Hacemos chistes con el cliente sobre el sitio web actual, firmamos los contratos y luego comienzan los planes y prototipos. Cuando se trata de proyectos pequeños este tipo de estrategias podrían funcionar. Pero cuando se trata de proyectos grandes, el crecimiento repentino y cambios no planeados después de la primera presentación. Y si llegas a terminar el proyecto, probablemente acabarás con una monstruosidad que se parece a lo que debías arreglar, sólo que éste lucirá un poco mejor que el original.

Un excelente diseño de flujo de trabajo comienza con un buen proceso de planificación.

Un excelente diseño de flujo de trabajo comienza con un buen proceso de planificación.

Un buen diseño y una buena experiencia de usuario no empiezan con un prototipo o maqueta atractiva, o con un plano, sino con una base conceptual que se centra en los objetivos de la empresa. Y dichos objetivos no son “Hagamos un sitio web más sencillo y bonito.” Como tú eres responsable de la UX y UI (Experiencia de Usuario e Interfaz del Usuario), por lo que es necesario asegurarse que los usuarios tengan la mejor experiencia en la interfaz que se está diseñando. Y para lograr esto, se debe planificar a fondo desde el principio.

Investigación Inicial para el Diseño de Páginas Web

Una vez que se termina la investigación inicial del campo del cliente y el de su competencia, lo siguiente es saber con qué se va comenzar. Se puede tener un precio separado para este análisis (que se incluirá en la cotización final del proyecto). Esto evitará cotizaciones altas y ambiguas que supuestamente incluirán cualquier ítem que salga más adelante.

El cliente que se tratará como ejemplo es una organización sin fines de lucro en el campo de la arqueología. La compañía explora y protege sitios arqueológicos en el sur-oeste del país. Actualmente, el sitio web está descomunalmente desorganizado. Imagina montañas y montañas de contenido poco organizado en un antiguo portal cms. Teniendo esto en cuenta, el primer paso es organizar el contenido.

Paso 1: Familiarización con el contenido

Cuando estás construyendo una casa nueva, no derrumbas todas las paredes sin antes sacar lo que está dentro de la misma. El primer objetivo sería ir dentro de todas las habitaciones y sacar todo el contenido. En diseño hablado (Design Speak), debemos ir a todas las entradas y determinar cuáles deberían ser estáticas y cuales tienen material para ser tipo post.

Al tener la información se puede hacer una estrategia para organizar mejor el sitio. En principio no se comprueba lo que debe ser eliminado definitivamente y que no, debido a que en el mundo real no se trabaja de esta forma. Cada parte del portal está asignado a un equipo de trabajo, debido a que hay mucho contenido. Lo mejor es clasificar todo primero.

Por lo general se comienza con la página principal y luego se van analizando el resto de las páginas. De esta forma, tengo un esquema del sitio web antes de la primera reunión. Aunque este no fue el caso para el proyecto que tenemos aquí, por qué la mayoría de los enlaces estaban escondidos en el contenido y no eran accesibles en la navegación inicial del portal, (si la cotización del proyecto se basó en la navegación principal o la página principal, este será el momento en el que te arrepientes). Una vez se habló con diferentes personas en el equipo del cliente, se pudo aclarar la situación para continuar.

Al final, la solución fue hablar con el cliente y tener una sesión de intercambio de ideas (brainstorming). Durante esta sesión se le pidió al cliente identificar las características, el contenido, el flujo de trabajo y el enfoque de la página web. Luego se establecieron los siguientes núcleos (core types), algunos son nuevos y otros ya estaban establecidos:

Better Website Design Workflow

La parte más difícil de este proyecto es que los enlaces no estaban disponibles a primera vista, sino ocultos dentro del contenido del sitio. Y una vez que se habló con el cliente y el equipo del mismo, se pudo continuar con el proyecto.

Paso 2: Creación del Enfoque, Simplificación y Organización

Con el contenido de la casa fuera de la misma, empaquetado y etiquetado, es momento de hacer los planos para una nueva casa que tendrá un mejor estilo y mostrará el contenido de mejor manera. Pero antes de llegar a ello, se debe crear el enfoque.

Según el cliente, los usuarios que visitan la página web buscan información sobre arqueología, tanto como sus proyectos de investigación, ver los próximos eventos, o leer la revista mensual. A pesar de que estas son las áreas de contenido es donde terminan los usuarios, el sitio web no se desenvuelve en torno a ellas; para poder encontrar el foco del portal, debes prestar atención al núcleo de la organización.

Se delimitó que la “localización” está en el centro de los contenidos. Sin la ubicación o localización no existirían los sitios arqueológicos, ruinas, museos o exposiciones. En el fondo, la arqueología se desenvuelve en torno a las ubicaciones.

Una vez se tiene el enfoque es momento de simplificar y organizar. Abajo, se despliega el contenido que se sacó del sitio que no tenía relación con ubicaciones, tales como las páginas del equipo, informes anuales, y así sucesivamente. Luego de ordenar y categorizar el contenido, se obtuvo este mapa:

Website Design Workflow

Ahora se tienen dos áreas principales “Cosas por hacer” y “Ubicaciones ”. En “Cosas por hacer” se tratan todos los temas que tienen que ver organización, mientras que en “Lugares” se cubre el contenido relacionado a una ubicación específica. Es posible que el usuario promedio no sepa el nombre del video o el del proyecto, pero si la ubicación de donde se desarrolló. Por ejemplo, en la sección de proyectos, el usuario podría encontrar el que le interesa a través de su ubicación.

Además, hay un color para cada tipo de post. Desde el punto de vista organizativo, los eventos, exposiciones, clases y exposiciones online son todos, esencialmente, eventos, sólo que hay diferentes tipos.

En el sitio actual había una página estática para una revista, y una para la tienda. Se decidió eliminar el paso adicional de ir a la tienda, y en su lugar tener una plantilla para artículos de la revista en la tienda. El resto es sencillo: una página con información acerca de la organización, un enlace directo a la tienda, una página de donaciones (por qué esas páginas generan ganancias y necesitan una sección en el navegador principal), también nuevas páginas para actualizaciones y un enlace directo a la tienda/donaciones. Debido a que esas páginas generan dinero, merecen un lugar en la navegación principal. Ahora se tiene el plano del proyecto, y es momento de vincular contenido a la misma.

Paso 3: Looping al Cliente

En el mapa del sitio exhibido con anterioridad, se incluyen los tipos de páginas pero no el mapa de los contenidos. Como de seguro ya sabrán, la mayoría de los problemas se producen una vez el cliente comienza a agregar contenido al sitio. Para evitar esta situación se hace un loop con el cliente desde el principio. Lo siguiente es crear un documento de Google con el mapa del sitio que tenemos, y luego enviárselo al cliente para que nos diga cuál es el contenido actual y cómo lo quiere mapear en la nueva estructura.

Si algo no encaja se puede resolver más adelante. Esto es uno de los pasos más importantes, debido a que no sólo involucra al cliente sino que también nos deja saber cualquier problema de la estructura antes de comience la implementación. Para el proyecto que estamos tratando, resultó que se cambiaron algunos elementos del mapa del sitio con respecto al menú; cómo el cliente tenía varias páginas de donaciones, la solución fue crear un post único para las mismas.

Creación de Estructura Visual a través de Wireframing

Lo siguiente es crear una estructura visual para el sitio. Para que la función del sistema trabaje exitosamente, y para lograr la idea de “todo relacionado a la ubicación”, se creó una relación bidireccional entre los diferentes tipos de posts.

Esta es la idea: Cuando un visitante llega al sitio de arqueología y escoge el Gran Cañón, no sólo verá la información sobre el lugar, sino que también encontrará los eventos, proyectos, exposiciones y cualquier otra cosa que el cliente haya enlazado en relación a la página del Gran Cañón. Y así terminamos con algo como esto:

Website Design Workflow

La página de índice de ubicaciones muestra los lugares más recientes en la parte superior. La barra de búsqueda es lo primero que verá el usuario al entrar en la página. Cuando el usuario entra en el sitio, podrá ver el mapa de Google en un 80% de la pantalla, y cuando escoja un punto en el mismo o van viendo la página, aparecerán la red de listas para facilitar su búsqueda.

En una página individual de ubicación la navegación principal estará a la izquierda, debido a que es la información más relevante, mientras que la información meta relacionada al lugar estará a la derecha. Cuando se diseña un plano, para obtener una buena composición se debe tener bloques de elementos primarios, secundarios y terciarios bien definidos. Así el ojo humano seguirá cada uno sin quedarse estancado en un solo elemento.

En este diseño, el usuario empezará en la cabecera del sitio, y luego continuará hacia el bloque de contenido, y después la información a la derecha. Cada segmento de contenido relacionado se muestra en orden de relevancia e importancia. Si el usuario está leyendo sobre el Gran Cañón, por ejemplo, probablemente a esto le seguirán fotos y un mapa del lugar.

Este sitio es esencialmente educacional, por lo que después vendrá “En relación a esta ubicación”. Debido a que el cliente no tiene mucha información única asociada con cada ubicación, se combinó el contenido apenas usado en un bloque exclusivo debajo del menú del sitio.

Colocando miniaturas de la revista y los videos en contenido relacionado añade elementos visuales adicionales y guía a los usuarios hacia la página de “compra”. La página está terminada al mostrarle las ubicaciones, ya que esto incentiva a los usuarios a seguir explorando el sitio web.

El siguiente paso es continuar con esta estructura para otros tipos de posts. Éstos son algunos de ellos:

Better Website Design Workflow

Ahora que se tiene un modelo general para los tipos de posts, me puedo concentrar en la página principal. Al igual que con todo interfaz de usuario, el primer paso es llegar a la meta de la página principal. La investigación del cliente mostró que muchos usuarios se tropiezan con el sitio sin entender completamente lo que es. Por lo tanto, una introducción y un texto de bienvenida debe ser lo primero que ven los usuarios. El nuevo enfoque central gira en torno a lugares y éste debe ser el bloque secundario, seguido de lo que está pasando en ArchaeologySouthwest.org (la revista actual, blog, eventos, noticias, etc.). Esta es la iteración del proceso de diseño:

Better Website Design Workflow

Con V1, se creó un diseño básico que se asemeja a la página inicial. No hay mucha jerarquía; lo primero que un usuario verá es el lugar destacado y luego se perderá en las columnas. Con V2, se creó una columna separada que hace que sea más fácil de seguir. Sin embargo, todavía se puede mejorar. Aquí es donde el conocimiento del contenido juega un papel importante. Se sabe que ArchaeologySouthwest.org no tiene más de dos eventos sucediendo a la vez, por lo que no tiene sentido tener muchos en la página de inicio. En V3, el foco está en los próximos eventos de manera que si va a haber más de dos, el usuario puede hacer clic en un botón “más” y ver el resto. También se hizo un énfasis adicional en la revista actual, ya que es fuente de dinero del cliente. Los usuarios comienzan en la parte superior y con el uso de un patrón F, se mueven hacia abajo. Así que ahora, el flujo del ojo es Localización> Bienvenido> Revista> Eventos> Noticias.

Ahora que se tiene un wireframe visual y estructura del sitio es mucho más fácil solidificar características y cómo funcionarán las cosas. Se tiene otra reunión con el cliente para repasar la funcionalidad real (que ahora sale a la luz) y el flujo de la interacción del usuario. Se sabe que todavía habrá ajustes en el final, pero serán solo ajustes y no cambios en todo el proceso. Lo más importante es que no habrá sorpresas.

 

Diseño de páginas web

Ahora viene la parte emocionante. La conversión del wireframe a algo que la gente va a usar y apreciar. Con este diseño, se quería reforzar el flujo mucho más mediante el uso de las marcas de colores y la tipografía. La guía de estilo del cliente es el siguiente:

Website Design Workflow

Comenzando con la tipografía

La tipografía es la base de un buen diseño y es por eso que se determinó desde el comienzo. La mayor parte de su identidad utiliza los tipos de letra Univers Condensed Light y Adobe Caslon. No había reglas sobre cuándo Adobe Caslon se iba a utilizar, pero se notó que no se utiliza tan a menudo como Universe. Se llevó a cabo un pequeño estudio de la tipografía para ver qué uniones creaban el mejor ambiente para una organización sin fines de lucro, humilde y profesional, sin que se viera diferente en comparación con su garantía actual.

Al hacer comparaciones de la tipografía, está claro que Adobe Caslon hará un gran trabajo como tipografía del título, y Universe para subtítulos. Al tener los títulos principales en mayúsculas le da a la marca un toque más personal. Tenerlo todo en mayúsculas hace que la Arqueología se sienta como una entidad corporativa.

Better Website Design Workflow

Para los encabezados principales, se va a usar Adobe Caslon, y el uso de Univers Condensed Light para todo lo demás para que coincida con su garantía de marca actual.

La creación de la apariencia de la página web

Se quería crear una experiencia ligera y abierta para los usuarios, quienes deben sentir que esta organización sin fines de lucro los toma en serio sin ser fríos y corporativo.

Basándonos en la base de los datos de análisis, la mayoría de los visitantes entran desde computadoras de escritorio (probablemente porque la mayoría de las personas visitan el sitio para investigar) y, por tanto, el enfoque inicial se centró en el diseño para los usuarios de computadoras de escritorio. Este resultó ser el diseño final:

Al entrar desde una computadora de escritorio, quería que los usuarios vieran de inmediato la ubicación ofrecida, texto de bienvenida y los próximos eventos seguidos por parte del título de la revista. De esta manera la gente puede ver que hace la compañía, y lo que están promoviendo antes de que tengan que desplazarse en la mayoría de los dispositivos portátiles. Con el uso de una sombra suave en la columna izquierda se le da más atención y se solidifica la jerarquía.

En los dispositivos móviles las prioridades son un poco diferentes, ya que los usuarios tienen acceso a la información sobre la marcha, los eventos son más importantes y por eso están más arriba en la lista. El diseño completo termina así:

Se actualizó el botón de donación en el pie de página para que sea más cándido cambiándolo por una oración en lugar de un botón.

Se actualizó el botón de donación en el pie de página para que sea más cándido cambiándolo por una oración en lugar de un botón.

Creación del Resto

A continuación, se puede aplicar de igual modo, este concepto de diseño en nuestros otros diseños.

Sabemos con certeza que los usuarios llegan a la página de detalles por dos razones: o bien que quieren aprender más sobre el punto de referencia, o ya saben acerca de esta ubicación y solo buscan información (direcciones, números de teléfono, etc.). Por lo tanto, es importante presentar las dos opciones de inmediato para que nuestros usuarios no pierdan tiempo en la búsqueda.

Better Website Design Workflow

Se decidió romper la columna de datos fuera del área de contenido para darle más peso, así como hacer la página más interesante. Esto ayuda a crear una jerarquía de composición de modo que cuando el usuario llega a la página, ven primero el título, seguido por las imágenes de la galería y la columna de detalles meta a continuación. Esto asegura que notarán la información meta adicional inmediatamente. Un poco de arreglo adicional a la columna mantiene los ojos en ella y hace que sea más fácil de hojear la información.

El cliente no va a tener muchos videos y revistas relacionadas con cada ubicación, por lo tanto, sólo estamos exhibiendo artículos y si hay algo más pueden hacer clic en el enlace. La vista móvil colapsa como es de esperarse con el contenido apareciendo primero, y luego la meta-información. Los videos y revistas fueron hechos de último en nuestra página móvil, ya que son menos importantes para los usuarios de móviles. Otras secciones internas siguen esta misma estructura y wireframes para crear una buena experiencia y flujo constante.

Reflexionando sobre el proceso de diseño se puede ver que la mayoría del tiempo se gastó en la organización y la planificación. Mientras que solo el 30% del tiempo se dedicó al diseño. A menudo, cuando los diseñadores muestran su trabajo, son deshonestos acerca de cuánto tiempo se dedica a la organización de Google docs, en lugar de hacer mockup llamativos. Por lo tanto, muchos otros diseñadores saltan directamente a los mockup y terminan con proyectos descarrilados y clientes insatisfechos. No hay una forma única para planificar, sólo se tiene que hacer, si quiere tener un proyecto exitoso. Déjanos tus comentarios sobre cómo es tu proceso, o cómo se diferencia – Sería interesante ver los flujos de trabajo de los demás.

 

El artículo original lo pueden encontrar en Totpal.

29 Ago 2016

Construyendo una Compañía a Distancia

Por: JAN SCHULZ-HOFEN

Si me preguntan, trabajar a distancia es lo mejor hay. Actualmente estoy escribiendo desde un bar en una pequeña playa en una isla remota en el sur de Tailandia. Cuando levanto la vista de mi laptop, sólo veo el infinito océano y sus aguas cristalinas. Disfrutaré de esta mañana sin distracciones y me concentraré en mi trabajo porque el resto del equipo ni siquiera se ha despertado. Las zonas horarias funcionan muy bien para los equipos distribuidos.

Mi colega Thomas recientemente habló con 11 Líderes en Gestión de Proyectos (11 Thought Leaders in Project Management) sobre el impacto que genera trabajar a distancia en una compañía; algunos expertos en Scrum argumentaron que los equipos distribuidos pueden trabajar juntos de forma efectiva, mientras que otros estuvieron fuertemente en desacuerdo.

Entiendo sus preocupaciones; no puedes simplemente abrir las puertas de la empresa y liberar a todos en la jungla. No está garantizado que terminarás con un negocio próspero. Un ejemplo claro es el de Marissa Mayer en Yahoo, en el famoso recorte laboral de trabajo a distancia en 2013 (axed remote work), cuando sintió que algunos empleados estaban abusando del mismo.

Entonces, ¿cómo puede lograr una compañía tecnológica el trabajo a distancia? Continúa leyendo. La siguiente historia está basada en nuestra experiencia en Planio y cómo logramos que el trabajo a distancia funcionara.

Entra en Planio, mi Compañía a Distancia

Existen una cantidad de cosas que me motivaron a comenzar mi compañía. Separarme del trabajo del cliente, al tiempo que mantenía todos los beneficios de trabajar como freelancer sin una ubicación fija fue uno de los motivos.

En 2009, me encontraba bajo la sombra de unos cipreses en un hermoso jardín al estilo mediterráneo, desde donde podía ver las colinas toscanas, mientras trabaja arduamente en un nuevo proyecto: Planio.

En una herramienta de gestión de proyectos para personas como yo: desarrolladores. Planio ayuda a crear proyectos de clientes de forma más organizada y transparente, mientras reduce el número de herramientas y plataformas que se necesitan para realizar el trabajo. Planio está basada en Redmine de código-abierto (Un proyecto de software de código abierto basado en Ruby on Rails*), el cual he utilizado a distancia con mis clientes desde sus comienzos. En cierta forma, el trabajo a distancia ya se encuentra en ADN de Planio.

Hoy en día, mi pequeño proyecto paralelo ha crecido hasta convertirse en una empresa de verdad. Somos un equipo de 10 personas, prestando servicio a más de 1,500 negocios en el mundo. Tenemos una oficina en Berlín, pero muchos de nosotros trabajamos a distancia.

En este artículo, indagaré en los principios, herramientas y lecciones que me han ayudado a lo largo de los años. Después de leerlo, espero puedas diseñar el software de tu compañía para que sea remote-friendly desde el principio.

“Hablar no cuesta nada. Muéstrame el código.” Linus Torvalds

Cada Jueves tenemos una conferencia por video-llamada (All-hands conference) donde discutimos lo que hicimos la semana anterior, y lo que haremos a futuro.

Al principio, pasamos mucho tiempo discutiendo ideas antes de decidir qué hacer, pero descubrimos que es mucho más difícil cuando algunos miembros del equipo tienen una mala conexión a Internet y no puedes verlos.

Ahora usualmente solo “hacemos las cosas” y después lo discutimos, creamos un prototipo que funcione con algunas ideas esenciales y después hablamos sobre eso. Por ejemplo, recientemente nos encontramos con algunos problemas de desempeño con nuestros repositorios de Git. En lugar de discutir y analizar todas las formas en las que podíamos ahorrarnos un par de milisegundos por aquí y allá con cada propuesta, mi colega Holger simplemente construyó sus mejoras sugeridas (Speeding up Git at Planio) en una prueba de concepto en un servidor en staging hacia donde dirigimos parte de nuestro tráfico. Esto resultó bastante bien y estas ideas irán a producción.

Este método hace que todos se enfoquen en la acción en vez de hablar. El tiempo invertido en escribir código es recompensado al ver que pasamos menos tiempo andando con rodeos.

Utiliza la Comunicación Escrita

La comunicación en tiempo real inhabilita la claridad. Llamar a un compañero cuando necesitas algo es fácil e instintivo, pero no es siempre tu mejor opción. No puedo recordar cuantas veces empecé a escribir un correo o una nota de Planio para luego resolver el problema mientras redactaba.

Zach Holman, uno de los primeros ingenieros contratados en GitHub, está de acuerdo en que: “El texto es explícito. Al forzar la comunicación a través de un método escrito, fuerzas a las personas a formular mejor sus ideas.”

La comunicación escrita también te hace más respetuoso del tiempo de los demás, en especial cuando vives a varias zonas horarias de distancia. Una comunicación inmediata puede disruptiva; puede ser que la persona se encuentre tratando de averiguar porque un sistema o proceso no funcionó correctamente. En cambio con un correo, él o ella puede leerlo cuando le sea más conveniente, y dar una respuesta adecuada.

Se tan Transparente como sea Posible

Pasar mucho tiempo preocupándose por las políticas de oficina no es la forma más propicia para enviar un software que funcione, y la claridad o transparencia promueve la confianza. No es una coincidencia que compañías a distancia, como Buffer, tengan una transparencia radical. En el caso de Buffer, la empresa comparte información sobre los ingresos y salarios de todos sus empleados.

Automattic, la compañía detrás WordPress.com, también pone énfasis en la transparencia. En el libro “El Año sin Pantalones” (The Year Without Pants), Scott Berkun comparte su experiencia trabajando a distancia para Automattic, y que todas las decisiones y discusiones están disponibles para los empleados en su plataforma de debate P2 como parte de su énfasis en la transparencia.

La función de chat en Planio funciona de forma similar. Los debates están abiertos para todo el mundo y los historiales de chat están conectados automáticamente desde los problemas que se discutieron para que nadie quede de lado; incluso los nuevos empleados pueden leer las decisiones que se tomaron anteriormente y por qué. Cuando comencé a desarrollar la función para chatear, considere agregar una función para chatear en privado con otros, pero cuando los debatimos en equipo, llegamos a la conclusión de que era mejor no hacerla para tener la mayor transparencia posible en el equipo.

Me parece que la transparencia en equipo es fundamental para equipos remotos. Por ejemplo, imagina por un momento que acabas de unirte a un equipo de desarrolladores a distancia. Quizás nunca has conocido a tus nuevos colegas. No conoces las reglas tácitas de comportamiento. Puede ser que te preocupe si estás realizando un buen trabajo o no. ¿Tus compañeros de trabajo están siendo sarcásticos o sus cumplidos son en realidad sinceros? ¿Estarán discutiendo en privado si eres un buen ingeniero?

Digitaliza tus Sistemas

Escogemos nuestros servicios de acuerdo a lo que ofrecen en plataformas online, desde los proveedores de teléfono hasta los bancos (algunos incluso ofrecen un pequeño incentivo financiero si decides dejar los papeles, además es genial para el medio ambiente). Yo tengo la fortuna de tener un abogado y un contador para Planio a los que no les importa enviar correos o mensajes por Google Hangouts, en lugar de decirme que vaya hasta sus oficinas. (En realidad recomiendo preguntar sobre ello en la primera reunión). Y puntos extra si se logra que firmen con una herramienta de gestión de proyectos y formen parte de tu equipo.

Hemos estado digitalizando nuestro correo postal; en Planio usamos un servicio llamado Dropscan, el cual recibe nuestras cartas, las escanea y reenvía las más importantes a las personas indicadas. No te gustaría que un amigo las agarrara y las leyera en Skype. Si no puedes encontrar un proveedor que escanee tu correo postal en tu país o ciudad, algunos sitios ofrecen membresías virtuales para tener una dirección postal física mientras no estés en la zona.

Para aquellas compañías que aún siguen enviando correo, existen servicios para que nunca haya necesidad de visitar una oficina postal nuevamente. Nosotros utilizamos una empresa de imprenta Alemana con una API (interfaz de programación de aplicaciones) que automáticamente envía una carta junto con una calcomanía a cada nuevo cliente de Planio. Es algo que a la gente le encanta, y no tenemos que imprimir y enviar nada. Algunas alternativas internacionales son Lob y Try Paper.

¿Deberías tener una Presencia Digital?

En un co-working en la Isla tropical Koh Lanta, Tailandia, me di cuenta que alguien en rol de apoyo para una gran plataforma de e-commerce estaba constantemente en una video conferencia con el resto del equipo. Sqwiggle ofrece una función similar de “presencia” para equipos remotos.

Supongo que demandar que todos los empleados estén “presentes” mediante video mientras trabajan puede basarse en el miedo de que los empleados abusen de los beneficios del trabajo a distancia. En mi experiencia, este no es el caso. En el co-working tropical se siente cierta urgencia en el aire, a pesar de los atuendos casuales y los cócteles de coco. Las personas están concentradas tranquilamente en sus laptops; es como si quisieran que el trabajo a distancia funcione y muestre resultados para mantenerse lejos de una oficina para siempre.

Nos dimos cuenta que no necesitamos una presencia digital porque tenemos un nivel de confianza muy alto con todo el equipo. También me parece que es importante respetar la privacidad de todos. Si tu empresa está haciendo el cambio de oficina física a trabajo remoto, la presencia digital puede ayudar a calmar esos problemas de desconfianza en los gerentes más ansiosos.

Escoge Bootstrapping en lugar de Capital de Riesgo

La mayoría de los capitalistas de riesgo buscan resultados descomunales, por lo que prefieren un pequeño estallido de 12-meses de trabajo de un equipo en lugar de un ritmo más sustentable. Front App, una nueva empresa fundada por Y Combinator, la aceleradora de Silicon Valley, alquiló una casa en la bahía durante su estadía de tres meses en el programa de la misma aceleradora. La meta es optimizar para evaluar una idea empresarial rápidamente.

Dada esta mentalidad de resultados desmesurada, puede ser que sea algo difícil convencer a un capitalista de riesgo que financie una estadía en un playa de Camboya. Es por ello que muchas empresas respaldadas (como Buffer o Treehouse) que se dedican al trabajo a distancia, primero tenían ventajas. Buffer era rentable antes de que se invirtiera en ella, mientras que Ryan Carson, el fundador de Treehouse, ya había demostrado sus habilidades con otra compañía.

Pero he aquí otra opción que es mejor que capitalismo de riesgo: Bootstrapping. Lo cual significa financiar una empresa con los ingresos de sus primeros clientes. En mi opinión, es el mejor enfoque porque permite fundar tu empresa con tus propios términos y mantenerte en control. Sin embargo, muchas veces se requiere tener dos trabajos o trabajar de forma freelance mientras se saca la compañía a flote. Me tomó aproximadamente dos años trabajando en Planio y proyectos para clientes (con mi agencia desarrollo de software LAUNCH/CO) para poder empezar de lleno, pero valió la pena.

Bootstrapping también te fuerza a construir un negocio que genere ingresos desde el principio, lo cual me parece mucho más saludable. Una pista: Construir un B2B SaaS hace que todo sea mucho más fácil que crear una aplicación de consumidores, debido a que las empresas están más dispuestas a pagar suscripciones mensuales si se le agrega importancia. Se deben vender muchas aplicaciones de consumidores en iPhone a $0.99 para cubrir un mes de pago de así sea el equipo más pequeño.

Bootstrapping también te fuerza a construir un negocio que genere ingresos desde el principio.

Ponle el Precio a tus Productos Estratégicamente

Uno de nuestros primeros clientes era una gran empresa tecnológica con billones de ingresos al año. Obviamente, estaba encantado cuando nos escogieron por encima de competidores más grandes y mejor establecidos. Sigue siendo un cliente satisfecho, pero ahora nos hemos alejado de grandes empresas; he descubierto que se necesitan muchas reuniones en persona y se los lleve de la mano antes de convertirse en un cliente.

Como dice Jason Lemkin en su artículo sobre cómo escalar al éxito para SaaS (Scaling Your Customer Success Team), cuando tienes una gran empresa, alguien tendrá que tomar un jet dos veces al año para visitarlos. Si tienes una empresa pequeña de dos o tres personas, esa persona serás tú, el CEO, el CMO y el CSO todos-en-uno, el hámster con exceso de trabajo.

Manteniendo los precios en el rango modelo de unos $49/$99/$249 como ha sugerido el desarrollador ahora empresario Patrick McKenzie, esto significa que no se tendrá que contratar un equipo empresarial de ventas, y luego tener que ganar la inmensa cantidad de dinero que un equipo así requiere. Tú, el cliente, no esperaras que el CEO de una empresa aparezca en Navidad con una caja de chocolates cuando estas pagando $249 al mes.

Desarrolla en Código Abierto

Un negocio con respaldo de riesgo basado en software propietario está muy bien cuando juegas “El Ganador se lleva Todo” y eres dueño del mercado. Cuando eres una compañía de bootstrap, el software de código abierto puede darte ventajas y alcance que no podrías haber logrado de otra forma.

Existe una precedencia de empresas tecnológicas rentables construyendo negocios en base a código abierto; el famoso código abierto Rails de Basecamp les ha garantizado ingenieros de alta calidad para el resto de la eternidad. GitHub se ha convertido en un unicornio, aprovechando el proyecto de código abierto Git que Linus Torvalds comenzó a gestionar con las fuentes de Kernel de Linux. Nuestros amigos en Travis-CI comenzaron un proyecto de código abierto, crearon una campaña en Crowdfunding y luego lo convirtieron en un negocio de bootstrap a distancia (la cual también hace campaña para la diversidad tecnológica a través de su fundación).

Planio está basada en Redmine y nosotros contribuimos muchas de nuestras características y mejoras a la comunidad. Esto funciona muy bien en muchas formas; nuestras contribuciones y compromiso con la comunidad ayuda a que el proyecto de código abierto avance y Planio se exhibe a potenciales clientes. Para nosotros, es la forma más auténtica de desarrollar la marca; al mostrar nuestro código y responder las discusiones técnicas abiertas, demostramos que sabemos lo que estamos haciendo.

Contrata Profesionales Certificados

Contratar una flota de pasantes cada año solo tiene sentido si se planea que el número de empleados crezca tan pronto como se llegue a la próxima ronda de financiación.

La externalización de tareas es fácil si se trata de copiar-y-pegar, pero en realidad no se quiere externalizar losDevOps (Desarrolladores y Operaciones) a alguien con la tarifa más baja por hora, cuando tienes miles de clientes dependiendo de tus servidores: querrás tener profesionales certificados como los que se encuentran en Toptal.

Matt Mullenweg, fundador de la plataforma de código abierto para blogs WordPress, declaró que al concentrarse en la calidad significó que su compañía, Automattic, pudiese contratar a candidatos experimentados que manejan el ambiente de trabajo no estructurado de una compañía a distancia.

Eso significa que “audiciona” candidatos dándoles un trabajo pago en un proyecto por varias semanas, y luego los contrata basándose en su desempeño. Automattic descubrió que este método es mucho más efectivo que los tradicionales CV’s y cartas de presentación para encontrar los candidatos adecuados.

Resalta la Calidad de Vida

Trabajar toma una increíble cantidad de tiempo año tras año. No debería ser algo que haces solo para terminarlo y ya; si es así, probablemente terminarás desperdiciando gran parte de tu vida. La mejor fuente de inspiración y el ingrediente principal para grandes resultados es un ambiente de trabajo que sea inspirador, agradable y divertido. Viajar, aprender, y socializar con otras personas de diferentes culturas hace que el trabajo sea menos como un sacrificio o un mal necesario (al menos en mi vida), que cuando trabajas en una oficina de nueve a cinco.

Trabajar toma una increíble cantidad de tiempo, año tras año. No debería ser algo que haces solo para terminarlo y ya.

No se trata solo de viajar por el mundo, aunque también está el aspecto de la libertad personal. Los padres pueden pasar más tiempo con sus hijos gracias a que evitan un viaje de dos horas. No tienes que vivir en Silicon Valley para ganar el sueldo de San Francisco. Tal vez tu otra mitad también obtenga un trabajo en el extranjero y no tengas que estar limitado a la terrible decisión de quedarte con tu trabajo y continuar tu carrera, o convertirte en un “cónyuge de traslado” con opciones profesionales limitadas.

Artículo original: https://www.toptal.com/business-intelligence/bootstrapped-construyendo-una-compa%C3%B1%C3%ADa-a-distancia/es

28 Jun 2016

Init.js: Una guía de los Por Qué y Cómos en el conjunto de tecnologías de JavaScript

 

La Historia

Entonces, tú y tu cofundador tienen esta genial idea para un negocio, ¿verdad?

Has estado agregando características en tu cabeza.

Frecuentemente, le preguntas a tus potenciales clientes sus opiniones, y todos ellos la aman.

Ok, entonces la gente la quiere. Hay hasta un poco de dinero para hacer. Y la única razón por la cual ellos no pueden obtenerla es porque no las has implementado—todavía.

Entonces, finalmente, te sientas un día y dices “¡Hagámoslo!”. Pronto, estás tratando de averiguar cómo aplicar la lógica de negocio de tu aplicación, la funcionalidad asesina que va a llevar adelante al producto: tienes una idea de cómo hacerlo, y ahora sabes que puedes hacerlo.

“¡Listo!¡Funciona!” dices. ¡Tu prueba de concepto es un éxito! Todo lo que queda por hacer es empaquetarlo en una aplicación web.

“Ok, hagamos el sitio”, dices.

Y entonces, te das cuenta de la verdad: necesitas elegir un lenguaje de programación; necesitas elegir una plataforma (moderna); necesitas elegir algunos frameworks (modernos); necesitas configurar (y comprar) espacio, base de datos y proveedores de alojamiento; necesitas una interfaz de administración; necesitas un sistema de permisos; necesitas un administrador de contenidos.

Quieres ser escueto, quieres ser ágil. Quieres usar tecnologías que te ayudaran a tener éxito en el corto-y largo-plazo. Y no son siempre fáciles de elegir

Tienes que tomar decenas y decenas de decisiones de arquitectura . Y quieres tomar las correctas: quieres usar tecnologías que te permitan desarrollo rápido, iteración constante, máxima eficiencia, velocidad, robustez y más. Quieres ser escueto, quieres ser ágil. Quieres usar tecnologías que te ayudaran a tener éxito en el corto-y largo-plazo. Y no son siempre fáciles de elegir.

“Estoy abrumado”, dices, mientras te vas sintiendo abrumado. Tu energía no es la misma de antes. Tratas de encajar las piezas, pero es demasiado trabajo.

Tu prueba de concepto se marchita y muere lentamente.

La Propuesta

Luego de abandonar toneladas de ideas de esta forma, decidí diseñar una solución. La llamo el proyecto ‘Init’ (Inicio)(ó init.js).

El corazón de la idea es tener un simple proyecto que inicie todos los demás, dejar que el desarrollador o el fundador técnico tomen esas decisiones al mismo tiempo y recibir una plantilla apropiada para empezar basada en esas decisiones. Se lo que van a decir los detractores, “Una solución no puede aplicarse a todos los problemas” (Odiadores odiarán). Y puede que estén en lo cierto. Pero podemos hacer nuestro mejor esfuerzo para crear una solución aproximada, y creo que Init se acerca bastante.

Para lograr mejor este objetivo, tenemos que tener algunas ideas claves en mente. Cuando estaba desarrollando Init, consideré:

  • ComponentesLa modularización es una característica clave de cualquier sistema ya que te permite reusar componentes de software a través de distintos proyectos—lo cual es el principal objetivo de Init. Pero la modularización también viene con una “reemplazabilidad” por producto, la cual será nuestra mejor aliada a la hora de atacar varios tipos de problemas con “casi” la misma solución.
  • Facilidad de DesarrolloPara algunos problemas, en algún lado hay una mejor solución escrita en [Brainf*ck](https://en.wikipedia.org/wiki/Brainfuck). ó jodecerebros). Pero implementar esa solución (en Brainf*uck) sería casi imposible de escribir, y mucho menos de leer. Te costaría tiempo y una enorme cantidad de esfuerzo. En general, deberías usar lenguajes y plataformas que hagan al desarrollo fácil, y no difícil para tí (o alguien que puede trabajar con él más tarde).
  • ComunidadCualquier plataforma que elijas, asegúrate que tenga una gran comunidad, y una que te pueda ayudar con los problemas más comunes y con los que no lo son tanto. Recuerda: jQuery puede no ser la librería másrápida, la más limpia, o la más elegante—pero es un ganador sólo por su comunidad.

Teniendo estos objetivos en mente, voy a mostrarte como hice mis propias decisiones al crear Init.

En su núcleo, Init se aprovecha del paradigma ‘full-stack JavaScript’ (algunas personas se refieren a él, o a una parte de él, como el MEAN Stack). Al trabajar con este conjunto, Init es capaz de usar solamente un sólo lenguaje mientras crea un ambiente increíblemente flexible y con todas las funciones para desarrollar aplicaciones web. En resumen, Init te permite usar JavaScript no solamente para desarrollo del lado cliente y servidor, pero también para construir, testear, maquetar, y más.

Pero bajemos un poco la velocidad y preguntémonos: ¿es realmente una buena idea usar JavaScript?

Por qué elegí JavaScript

Soy desarrollador web desde 1998. Por esas épocas usabamos Perl para la mayoría de nuestro desarrollo del lado del servidor, y aún desde esos tiempos teníamos JavaScript del lado del cliente. Las tecnologías web del lado servidor han cambiado inmensamente desde entonces: fuimos a través de oleada tras oleada de distintas tecnologías y lenguajes cómo PHP, ASP, JSP, .NET, Ruby, Python, solo por nombrar algunas. Los desarrolladores comenzaron a darse cuenta que usando dos distintos lenguajes para ambientes cliente y servidor estaba complicando las cosas. Los intentos iniciales para unificar bajo un mismo lenguaje intentaban crear componentes cliente del lado del servidor y compilarlos en JavaScript. Esto no funcionaba como se esperaba y muchos de esos proyectos fallaron (por ejemplo, ASP MVC reemplazando los formularios web de ASP.NET, y podría decirse queGWT será reemplazado por Polymer). en un futuro cercano). Pero era una excelente idea, en esencia: un lenguaje único en el cliente y el servidor, permitiéndonos reusar componentes y recursos (esta es la clave:recursos).

La respuesta era simple: usar JavaScript en el servidor.

De hecho, JavaScript nació con JavaScript Server Side en Netscape Enterprise Server, pero el lenguaje simplemente no estaba listo en esa época. Luego de años de prueba y error, Node.js finalmente emergió, lo cual no solo puso a JavaScript en el servidor, pero además promovió la idea de programación no-bloqueante, cambiando la forma en la que escribimos “fread”(I/O) para siempre (lee más aquí.

En una oración: la programación no-bloqueante apunta a poner tareas que consumen tiempo a un lado, usualmente especificando que se debería hacer una vez que estas tareas se hayan completado, y permitiendo que el procesador maneje otros pedidos mientras tanto.

Pero esas ideas no eran nuevas—entonces, ¿por que se volvieron tan populares con Node.js? Simple, la programación no-bloqueante puede ser alcanzada de distintas formas. Tal vez la más fácil es usar callbacks y un evento en bucle. En la mayoría de los lenguajes, esa no es una tarea fácil: mientras que los ‘callbacks’ son una característica común en algunos lenguajes, un evento en bucle no lo es y usualmente te encuentras aferrándote a librerías externas (por ejemplo: Python, con Tornado). Pero en JavaScript, los callbacks son construidos dentro del lenguaje, como también el evento en bucle, y casi cualquier programador que haya incursionado en JavaScript está familiarizado con ellos (o al menos los han usado, aunque no entiendan del todo que significa un evento en bucle). De repente, cada una de las startup en el Planeta Tierra podía reusar desarrolladores (por ej., recursos) tanto en el lado cliente cómo en el lado del servidor, resolviendo el problema de “Se necesita Gurú Python”.

De repente, cada una de las startup en el Planeta Tierra podía reusar desarrolladores (por ej., recursos) tanto en el lado cliente cómo en el lado del servidor, resolviendo el problema de “Se necesita Gurú Python”.

Entonces, ahora tenemos una plataforma increíblemente rápida (gracias a la programación no-bloqueante) con un lenguaje de programación que es increíblemente fácil de usar (gracias a JavaScript). Pero, ¿Es suficiente? ¿Perdurará? Estoy seguro que JavaScript va a tener un importante lugar en el futuro. Déjame decirte por qué:

  • Programación FuncionalJavaScript fue el primer lenguaje de programación que llevó el paradigma funcional a las masas (por supuesto, Lisp llegó primero, pero la mayoría de los desarrolladores nunca han construido una aplicación en Lisp lista para ser usada en producción). Lisp y Self, influencias principales de Javascript, están llenos de ideas innovadoras. Esas ideas pueden liberar nuestras mentes para explorar nuevas técnicas, patrones y paradigmas. Y todos ellos llevan a JavaScript. Mira monads, codificación Church, o incluso (para un ejemplo más práctico) la colección de funciones](http://underscorejs.org/#collections), de Underscore.js las cuales pueden salvarte líneas y líneas de código.
  • Objetos Dinámicos y herencia PrototipadaLa programación orientada a objetos sin clases (y sin las interminables herencias de clases) permite rápido desarrollo (crear objetos, agregar métodos y usarlos) pero, lo más importante, reduce el tiempo de refactorización durante tareas de mantenimiento dejando que el desarrollador modifique instancias de objetos en vez de clases. Esta velocidad y flexibilidad pavimenta el camino para el desarrollo rápido.
  • JavaScript es InternetJavaScript fue diseñado para Internet, ha estado aquí desde el principio, y no va a irse. Todos los intentos por destruirlo han fallado, mira, por ejemplo, la caída de los Applets Java, el reemplazo de VBScript de Microsoft, TypeScript (que compilaba a JavaScript), y la caída de Flash en manos del mercado móvil y HTML5. Es imposible reemplazar Javascript sin romper millones de páginas web, así que nuestro objetivo a largo plazo debería ser el de mejorarlo. Y no hay nadie mejor para esa tarea que el Technical Committee 39 de ECMA.Ok, alternativas a JavaScript nacen todos los días, cómo CoffeeScript, TypeScript, y los los millones de lenguajes que compilan a JavaScript. Esas alternativas pueden ser útiles para etapas de desarrollo (por medio de mapeos de código fuente), pero fallarán al tratar de suplantar JavaScript a largo plazo por dos razones: sus comunidades nunca serán más grandes, y sus mejores funcionalidades serán adoptadas por el script de ECMA (por ej., JavaScript). JavaScript no es como un lenguaje ensamblador: es un lenguaje de programación de alto nivel con código fuente que puedes entender—entonces deberías entenderlo.
Ahora, gracias al proyecto Esprima, puedes crear tus propias herramientas para jugar con el código fuente, modificándolo, cambiando su estilo, agregando comentarios, instrumentando, y todo de tipo de cosas que puedas imaginar al jugar con el Árbol de Sintaxis Abstracta de tu programa como si estuvieses jugando con un Árbol DOM.

JavaScript de extremo a extremo: Node.js y MongoDB

Entonces, esas son las razones para usar JavaScript. Ahora, voy a usar JavaScript como una razón para usar Node.js y MongoDB.

  • Node.jsNode.js es una plataforma para construir aplicaciones de red rápidas y escalables—eso es básicamente lo que dice el sitio de Node.js. Pero Node.js es más que eso: es el entorno de ejecución preferido por cualquier aplicación con acceso de E/S en JavaScript. Incluso si no planeas escribir tu aplicación del servidor principal con Node.js, puedes usar herramientas creadas construidas encima de Node.js para mejorar tu proceso de desarrollo. Por ejemplo: Mocha.js para unit testing, Grunt.js para tareas de construcción automatizadas, o incluso Brackets para completar edición de código.Entonces, si vas a escribir aplicaciones de JavaScript para servidor o cliente, deberías familiarizarte con Node.js, porque vas a necesitar usarlo diariamente. Hay algunas interesantes alternativas), pero ninguna de ellas llega siquiera al 10% de la comunidad de Node.js.
  • MongoDBMongoDB es una base de datos NoSQL basada en documentos que usan JavaScript como su lenguaje de consultas, permitiendo completar de extremo-a-extremo la plataforma JavaScript. Pero esa no es siquiera la razón principal para elegir esta base de datos.MongoDB es una base de datos sin esquema que permite persistir tus objetos de una manera flexible y por lo tanto adaptarse más rápidamente a los cambios en los requisitos. Además, es altamente escalable y basado en map-reduce, lo cual lo hace adecuado para aplicaciones con muchos datos. MongoDB es tan flexible que puede ser usado como una base de datos de documentos sin esquema, un almacén de datos relacional (aunque le faltan transacciones),o incluso almacenamiento de clave-valor para respuestas de caché.

Modularización de Servidor con Express.js

Modularización en el lado del servidor nunca es fácil. Pero con Express.js (y Connect.js) vino la idea de‘middleware’(ó software intermedio). En mi opinión, middleware es la mejor forma de definir componentes en el servidor. Si quieres compararlo con un patrón conocido, se acerca bastante a los tubos y filtros.

La idea básica es que tu componente es parte de una tubería. La tubería procesa el pedido (entrada) y genera una respuesta (salida), pero tu componente no es responsable por la respuesta completa. En cambio, solo modifica lo que necesita y luego delega hacia la otra pieza de la tubería. Cuando la última pieza de la tubería termina su proceso, la respuesta se envía al cliente.

Nos referimos a estas ‘piezas de tubería’ como ‘middleware’. Claramente, podemos crear dos tipos demiddleware:

  • Intermedios: Esos que procesan el pedido y la respuesta, pero no son del todo responsables por la respuesta en sí, entonces delegan al siguiente middleware.
  • Finales: Esos que toman la responsabilidad por completo en la respuesta final. Ellos procesan y modifican el pedido y la respuesta, pero no necesitan delegar al siguiente middleware. En la práctica, se recomienda que delegues a un siguiente middleware, de todas maneras, para permitir flexibilidad en la arquitectura (por ej., agregar más middleware después), aunque ese middleware no exista (en ese caso la respuesta irá directamente al cliente)

Como ejemplo concreto, considera un componente ‘usuario administrador’ en el servidor. En términos de middleware, tendríamos tanto finales como intermediarios. Para nuestros finales, tendríamos características tales como crear un usuario y listar usuarios. Pero antes que podamos realizar esas acciones, necesitamos nuestros intermediarios para autenticación (ya que no queremos pedidos que creen usuarios sin autenticar). Una vez que creamos estos intermediarios para autenticación, podemos simplemente conectarlos en cualquier lado que queramos para convertir una característica sin autenticación existente en una con autenticación.

Aplicaciones de una sóla página

El proyecto Init enfoca en crear aplicaciones de una sóla página (SPAs-Single-Page Applications). Muchos desarrolladores web se han tentado más de una vez en probar construir SPAs. Desarrollé usando varias (principalmente propietarias), y puedo decir con confianza que son simplemente el futuro de las aplicaciones web. ¿Alguna vez comparaste una SPA con una aplicación web regular en una conexión móvil? La diferencia de respuesta es de decenas de segundos.

¿Alguna vez comparaste una SPA con una aplicación web regular en una conexión móvil? La diferencia de respuesta es de decenas de segundos.

Las SPA son el futuro de la web—¿entonces por que harías tu producto en un formulario antiguo? Un argumento común que escucho es que la gente está preocupada por el SEO. Pero si manejas las cosas correctamente, esto no debería ser un problema: Google mismo tiene un muy buen tutorial sobre como hacerlo, y hay muy buenos comentarios aquí también.

MV* del lado del cliente con Backbone.js, Marionette.js y Twitter Bootstrap

Mucho se ha dicho acerca de los MVC* frameworks para SPAs. Es una decisión complicada, pero voy a decir que el top 3 son Backbone.js, Ember.js, y Angular.js.

Los tres son bien considerados. ¿Pero cual de ellos es el mejor para tí?

Desafortunadamente, tengo que admitir que tengo una experiencia muy limitada con Angular.js, así que voy a dejarlo fuera de esta discusión. Ahora, Ember.js y Backbone.js representan dos maneras distintas de atacar el mismo problema.

Backbone.js es minimalista, simplista y te ofrece lo suficiente para crear una simple SPA. Por otro lado, Ember.js es un framework completamente profesional para crear SPAs. Tiene más adornos, pero también una curva de aprendizaje más grande.

Dependiendo del tamaño de tu aplicación, la decisión puede ser tan fácil como mirar el ratio de featuresUsed/featuresAvailable(características Usadas/Disponibles), lo cual te dará una gran pista.

En el caso de Init, quería cubrir la mayoría de los escenarios, así que elegí Backbone.js para creación fácil de SPAs, con Backbone.Marionette.View para modularización. De esta forma, cada componente es una simple aplicación, y la aplicación final puede ser tan compleja como queramos que sea.

Estilizar es también un desafío, pero podemos, de vuelta, contar con frameworks para rescatarnos. Para CSS, no hay nada mejor que Twitter Bootstrap, que ofrece un completo set de estilos que ya están listos para usar y sonfáciles de personalizar.

Booststrap fue creado usando el lenguaje LESS que es de código abierto, así que podemos modificarlo si así lo necesitasemos. Viene con una tonelada de controles de usabilidad que están bien documentados en el sitio de Bootstrap. Además, hay un modelo de personalización que te permite crear tus propios controles. Definitivamente es el hombre para este trabajo.

Mejores prácticas: Grunt.js, Mocha.js, Chai.js, RequireJS y CoverJS

Finalmente, deberíamos definir algunas de nuestras mejores prácticas, y buscar en como Init puede ayudarte a implementarlas y mantenerlas. Nuestra solución está centrada en varias herramientas, que están basadas en Node.js.

  • Mocha.js and Chai.js:Estas herramientas te permiten mejorar tu proceso de desarrollo aplicando TDD o BDD, proveyendo la infraestructura para organizar tus tests unitarios y un lanzador para correrlos automáticamente.Hay miles de frameworks para test unitarios para JavaScript. ¿Entonces, por que usar Mocha.js? La respuesta corta: es flexible y completo.La respuesta larga: tiene dos características importantes (interfaces, reporters) y una ausencia importante (assertions). Déjenme explicarles.
    • Interfaces: tal vez estés acostumbrado a los conceptos de TDD de suites y tests unitarios, o tal vez prefieras ideas BDD de especificaciones de comportamiento con “describe” y “it should”. Mocha.js te permite usar los dos acercamientos.
    • Reporters: correr tu test generará reportes de resultados, y puedes darle formato a esos resultados usando varios reporters. Por ejemplo, si tienes que alimentar un servidor de Integración Continua, puedes encontrar un reporter para hacer exactamente eso.
    • Falta de una librería de assertions: : lejos de ser un problema, Mocha.js fue diseñado para dejarte usar la librería de assertions que prefieras, ofreciendo más flexibilidad. Hay muchas opciones, pero ahí es donde Chai.js entra en acción.

    Chai.js es una librería de assertions flexible que permite usar cualquiera de los tres más importantes estilos de assertions:

    • Assert: Estilo de assertion clásico de la vieja escuela. Ej.:
        assert.equal(variable, ”valor”);  
      
    • Expect: Tipo de assertion encadenable más comúnmente usado en BDD. Ej.:
        expect(variable).to.equal(“valor”);
      
    • Should: También usado en BDD, pero prefiero Expect porque Should porque suena repetitivo con la especificación de comportamiento _’it _(“should do something..”-” eso debería hacer algo”). Ej.:
        variable.should.equal(“valor”);
      

    Chai.js se combina perfectamente con Mocha.js. Usando solamente estas dos librerías, puedes escribir tus test en TDD, BDD, o cualquier estilo imaginable.

  • Grunt.js:Grunt.js permite automatizar tareas de construcción, cualquier cosa desde simples archivos concatenados copiados y pegados, a plantillas precompiladas, estilo compilado de lenguaje (por ej., SASS y LESS), test unitario (con mocha.js), linting y compresión de código (ej., con UglifyJS o Closure Compiler). Puedes agregar tu propia tarea automatizada a Grunt, o buscar en el registro de Grunt, donde hay cientos y cientos de plugins disponibles (de vuelta, usando herramientas con grandes comunidades detrás paga bien). Grunt también puede monitorear tus archivos y disparar acciones cuando estos son modificados.
  • RequireJS:RequireJS puede sonar como otra forma de cargar modulos con AMD, pero puedo asegurarte que es mucho más que eso. Para entender por qué, primero debemos mencionar la idea del namespacing de modulos (ej., demo.views.hola), lo que evita contaminar el namespace global envolviendo cada módulo en su propio namespace. El problema es, estos módulos no son reusables: si modificas el namespace de una ‘instancia’, estás modificando el namespace de todas las ‘instancias’. En contraste con eso, RequireJS permite definir módulos reusables desde el principio. (Además, te ayudará a adoptar Dependency Injection para evitar que tus modulos accedan variables globales).
  • CoverJS:Cobertura de código es una medida métrica para evaluar tu testing. Como el nombre implica, te dice cuanto código está cubierto en tu conjunto de tests actual. CoverJS mide la cobertura de código de tus tests instrumentando declaraciones (en vez de líneas de código cómo JSCoverage) y generando una versión instrumentada de tu código. También genera reportes para alimentar tu servidor de integración continua.

Usando _Branches_ (_ramas_) para alternar características

Cuando empecé Init, necesitaba una manera para que los usuarios activen y desactiven varias características que podrían llegar a querer en su proyecto. Decidí tomar un enfoque radical con el sistema de ramas de git para implementar esta funcionalidad.

En esencia, cada rama representa una característica o funcionalidad que un usuario podría querer incluir. Si estás empezando un proyecto desde el principio, empieza por la rama mínima que necesitas, y luego agrega otras tecnologías fusionando la rama con las otras deseadas. Por ejemplo, digamos que quieres empezar tu proyecto con Backbone.js y Marionette.js. Bueno, puedes empezar en la rama Backbone.js y fusionarla con la rama Marionette, continuando desde ahí para cada pedazo de funcionalidad que quieras agregar.

Por ahora, la idea de fusionar para agregar funcionalidad puede solo ser usada para plantillas de tecnología (ej., Backbone, Node, Express). Pero en el futuro, serás capaz de alternar entre back-end (ej., desde MongoDB a Postgres) e implementaciones del lado cliente.

Empieza un proyecto con Init y haz un deploy en Heroku hoy

Nunca ha habido una manera más fácil de empezar un proyecto. Dirígete al repositorio de GitHub, fijate la rama con los últimos commits (ahora mismo es usermanager, aunque esto puede cambiar en el futuro) y entonces:

  1. Crea un directorio para tu proyecto (o usa uno existente).
  2. Crea tu repositorio con “git init” (o usa un repositorio existente)
  3. Agrega un servidor remoto con Init
     git remote add init git: //github.com/picanteverde/init.git
    
  4. Descarga la rama que quieras
    git pull init usermanager
    
  5. Obtén el archivo de procesos de Heroku
     git pull init heroku-webprocess
    
  6. Con el Heroku Toolbelt instalado, crea una aplicación
     heroku create
    
  7. Haz un push a la rama master a Heroku
     git push heroku master
    
  8. Visita tu aplicación en funcionamiento en Heroku!

Ahora puedes empezar a desarrollar tu característica asesina con solo unas líneas de código. No solo eso, sino que estarás desarrollando con las últimas y más eficientes tecnologías en una suite de desarrollo lo más automatizada posible.

Espero que puedas usar Init para comenzar tu próxima gran idea. Recuerda Revisar el repositorio de Init para ver correcciones y características—su desarrollo es bastante activo, y espero con ansias escuchar sus comentarios.

 

fuente: https://www.toptal.com/javascript/init-js-una-gu-a-de-los-por-qu-y-c-mos-en-el-conjunto-de-tecnolog-as-de-javascript/es