Feeds:
Entradas
Comentarios

Archive for the ‘dvcs’ Category


Aquí estoy de nuevo, esta vez con un post tecnológico. Voy a tratar de explicar en pocas palabras (a ver si no me sale un nuevo truño-post) lo que son los DVCS, que ventajas tienen y si nuestra alma corre algún peligro al usarlos.

Un Distributed Version Control System (DVCS) o en castellano, sistema de control de versiones distribuido, es la última moda en control de versiones de código, y al contrario de lo que yo pensaba en un principio, de distribuido tienen poco, de hecho es un nombre engañoso. Un DVCS no es más que un repositorio de código que puede sincronizarse con otros repositorios, pudiendo actuar tanto como cliente, tanto como servidor de otros repositorios. Lo más sencillo es comparar los DVCS con los tradicionales repositorios de código centralizados. Mantendré la discusión en este post a un nivel sencillo, de forma que cualquier usuario de repositorios tradicionales pueda entenderlo, para ello haré algunas simplificaciones, por favor, gurus del Git, no me crucifiqueis.

Tradicionalmente hemos usado repositorios centralizados. Un servidor central al que conectábamos nuestros entornos de trabajo. Cuando queríamos traer código desde el repositorio central a nuestro entorno de trabajo local, hacíamos un check out o update. Cuando queríamos mandar nuestros cambios al repositorio remoto hacíamos un check in o commit. Si queremos añadir nuevos ficheros o proyectos al repositorio central hacemos un share o add ¿Cómo hacemos todo ésto en nuestro nuevo y flamante DVCS? Pues exactamente igual: add, commit y update. Sí señor, en esto los DVCS son como cualquier otro repositorio.

En cuanto a los repositorios centralizados, los había principalmente de dos tipos:

  • Bloqueantes. Para poder trabajar con un fichero necesitas abrirlo en edición. Esto bloquea el fichero e impide que otro compañero, uy, quise decir usuario, pueda abrirlo en edición y trabajar con él. Cuando has terminado haces commit y el fichero se desbloquea. También puedes descartar cambios y desbloquear. La lógica detrás de este tipo de sistemas se basa en intentar disminuir la cantidad de conflictos y en la idea de que ciertos artefactos de código pertenecen a ciertos desarrolladores y nadie debería poder tocarlos (private code ownership). Con estos sistemas tienes menos conflictos, aunque también puede aparecer alguno de vez en cuando. También te lo pasas pipa cuando alguien se deja el fichero bloqueado y cae enfermo, se da de baja, está de vacaciones o en una reunión. Como veis este tipo de sistemas no promueven el agilismo.
  • No bloqueantes. Los más famosos son el CVS y el SVN. Simplemente te actualizas el fichero, trabajas sobre él, y haces commit de los cambios. Si tienes suerte no hay conflictos. Si no, tienes un conflicto, alguien modificó el fichero y lo subió antes que tu. Normalmente los conflictos se resuelven con un merge automático, pero a veces necesitas hacer un merge manual. Si usas integración continua no tendrás mucho problema, ya que al integrar cambios más frecuentemente, la cantidad de código sobre los que pueden haber conflictos es pequeña, con lo cual la probabilidad de éstos disminuye y la facilidad de resolverlos aumenta. De hecho la probabilidad de un conflicto y la facilidad para hacer el merge aumentan exponencialmente con la cantidad de código a integrar, lo que hace que si no usas integración continua seas hombre muerto. Si no usas integración continua, es mejor que uses un sistema bloqueante, y por lo tanto no podrás ser ágil.

Los DVCS son no bloqueantes (al menos todos los que yo conozco). Como veis los DVCS ese parecen mucho a un SVN o CVS, ¿dónde está la diferencia? Muy sencillo, los DVCS introducen dos nuevas operaciones:

  • La operación push. De la misma manera que podemos enviar cambios con un commit desde nuestra área de trabajo al repositorio, podemos enviar los cambios que hay en un repositorio hacia otro repositorio, si tenemos permisos claro. Esto puede generar que en el repositorio destino se tengan conflictos, es decir, distintos cambios sobre el mismo artefacto provenientes de distintos repositorios.
  • La operación pull. De la misma manera que actualizamos nuestro entorno de trabajo local con el contenido del repositorio central, podemos traer cambios desde otro repositorio hacia el repositorio origen del pull. Es la operación simétrica al push, y puede generar conflictos en el repositorio origen del pull.

Las operaciones push y pull pueden tener un commit en el destino implícito o no, dependiendo del DVCS que usemos y del comando exacto que lancemos. En caso de que no tengan commit implícito, debemos hacer commit manualmente si queremos aceptar los cambios.

Como hemos visto tanto el push como el pull pueden generar conflictos, que deben ser resueltos con merge. En los DVCS los algoritmos de merge están optimizados y suelen resolver automáticamente más conflictos que en un SVN o CVS, y tardan menos en ejecutarse.

Armados con estos conceptos podemos entender como se trabaja con un DVCS. Lo que se suele hacer es que cada desarrollador tenga uno o más repositorios instalados en su máquina local. Esto permite a cada miembro del equipo trabajar en local, con las consiguientes ventajas:

  • Se puede trabajar sin necesidad de conectividad, ya que el repositorio está en local.
  • Mayor rendimiento, ya que no hay necesidad de tráfico de red.
  • Mayor escalabilidad. No hay un servidor central que deba soportar la carga concurrente de distintos usuarios.
  • Sencillez en la gestión de los permisos. Cada desarrollador local sólo necesita dar permisos a los compañeros en los que confía.
  • No hay miedo a hacer commit, ya que el repositorio es local al desarrollador. Este miedo es sustituido por miedo a hacer pull o push, ya lo veremos en un momento.

¿Y lo de distribuido? Pues simplemente cuando dos desarrolladores quieren compartir código o integrar trabajo, hacen pull o push entre ellos.

¿No hay un repositorio central? ¿Quién controla el código del producto? Bueno, puedes tener un repositorio central si quieres, al que todos los repositorios locales entreguen código con pull o push. Pero puedes tener la topología o arquitectura de repositorios que quieras. De hecho es muy importante elegir la arquitectura de repositorios adecuada a implantar en tu proyecto. Yo recomiendo seguir la misma estructura que tienes en tu equipo de trabajo:

  • Si tienes un equipo plano, típico de Scrum, puedes usar una topología peer to peer. Todos los repositorios intercambian código entre si. Adicionalmente puedes añadir un repositorio principal de equipo y otro para las releases estables.
  • Si tienes un equipo jerárquico usa una topología jerárquica. Existe un repositorio central bendito (blessed) controlado por un jefe de proyecto. Este jefe de proyecto tiene lugartenientes con un repositorio cada uno. El jefe sólo incluye código en el repositorio central desde los repositorios de sus lugartenientes, y éstos desde los repositorios de sus subordinados.
  • Si tienes un equipo grande de Scrum, dividido en varios feature teams, cada feature team tendrá su topología de repositorios peer to peer, con un repositorio central por equipo. Los distintos repositorios centrales de los distintos equipos pueden conectarse a su vez en topología peer to peer o en modo jerárquico contra un repositorio principal de proyecto.

En general doy el mismo consejo que con la elección de estructura de equipo, si tienes varios repositorios, en una red rápida, y que deban intercambiar información frecuentemente, conéctalos en peer to peer para facilitar el intercambio de información y no tener intermediarios. Si el número de repositorios ya es grande, puedes añadir un nivel de jerarquía. A mi, en cualquier caso, me gusta tener siempre un repositorio central para cada equipo, y otro por proyecto si hubieran varios equipos.

¿Y como hacemos backup si no hay repositorio central? En esta forma de trabajar, el código importante e interesante, suele estar distribuido entre distintos repositorios. Esto hace que el sistema sea naturalmente tolerante a fallos. Esto no impide que hagas backup de repositorios que consideres especiales o que cada desarrollador haga backup del suyo. La política de backup depende de la topología escogida para los repositorios. Si usáis un repositorio por equipo y/o un repositorio por proyecto podéis hacer backup formal de éstos.

Seguro que todo esto tiene alguna pega… Cierto, al trabajar cada desarrollador en un repositorio diferente, se produce de forma implícita un branching agresivo del código. Es como si usáramos un repositorio centralizado, y cada desarrollador tuviera una rama diferente. Pensad en los posibles conflictos que pueden producirse cuando múltiples desarrolladores hagan push de sus cambios al repositorio del equipo o proyecto. Psicológicamente, el desarrollador, al trabajar contra su repositorio local, puede tener una falsa sensación de seguridad al tener un control de versiones respaldando sus cambios, y olvidarse de hacer push de sus cambios a sus compañeros o al repositorio del equipo. Por otro lado va a tener miedo, y ser renuente, a hacer pull de los cambios de los demás, no vaya a ser que le provoquen conflictos y le rompan su repositorio. Bien, no nos desesperemos, la solución es sencilla, entregar los cambios al repositorio de equipo de forma frecuente, es decir usar integración continua. Si usamos DVCS y no usamos integración continua estamos perdidos.

¿Espera un momento, integración continua dices, pero si no tenemos un repositorio central? Exacto, lo que realmente hace falta es… integración continua distribuida (o incremental) Este invento consiste en poner un demonio de integración continua en cada repositorio. Cada vez que se produce un commit, pull o push sobre el repositorio, el demonio se despierta y mira si hay conflictos. En caso de que los hubiera, intenta hacer un merge automático. Si el merge automático falla, el build se considera roto. Si funciona o no hubiera conflictos, se ejecuta un proceso de integración continua normal (compilación, tests, etc). Si el build es exitoso, entonces se envía una petición a un repositorio de más alto nivel, para que acepte los cambios. Este repositorio de más alto nivel suele ser el de equipo o el del proyecto. Si se aceptan los cambios (de forma manual o mediante una regla de decisión automática), se hace un pull para recibir los cambios, lo que vuelve a disparar el proceso de integración continua sobre éste repositorio. El proceso se repite recursivamente hasta que el build falla o se hace un build con éxito en el repositorio central de proyecto. Una idea interesante es mantener los builds de los repositorios de nivel de desarrollador sencillos y veloces, por ejemplo, sólo compilación y tests unitarios. El nivel de calidad exigido en los builds va creciendo conforme se va subiendo a repositorios de mayor nivel. Por ejemplo el repositorio de equipo puede exigir tests de integración para todas las historias de usuario. El repositorio de proyecto puede pasar una suite exhaustiva incluyendo despliegue en un servidor y tests de aceptación contra una infraestructura más real. De esta manera el build en local se mantiene ágil, y a su vez los cambios que van recibiendo los repositorios de mayor nivel tienen una mayor calidad, ya que han pasado los procesos de build continuo de los niveles anteriores.

Una etapa de Integración continua distribuida

Una etapa de Integración continua distribuida

Algunos estaréis pensando que este proceso de integración continua distribuida es demasiado complejo para ser implementado. Otros quizás penséis que es imposible poner un demonio para cada repositorio. Yo simplemente os digo que hace tiempo, cuando explicaba la integración continua simple, esto es, asociado a un repositorio centralizado, muchas veces me miraban con incredulidad y se solían escuchar las misma protestas: «demasiado complejo…», «demasiado para nuestros servidores…», «eso se va a caer…», «se va a formar un pollo de narices…», etc.

Otro punto a tener en cuenta, es que la integración continua simple, está siendo sustituida por integración continua multietapa, en organizaciones grandes, con repositorios centralizados. En la integración continua multietapa se realizan más de un proceso de integración continua de forma secuencial, donde cada etapa implica un nivel de calidad más estricto que el anterior. El objetivo es aumentar la escalabilidad del sistema de integración continua, ya que no se lanzan los procesos de integración continua más estrictos, y por lo tanto más pesados, sin tener un mínimo de seguridad de que vayan a tener éxito. Este mínimo de seguridad se consigue ejecutando antes procesos de integración menos estrictos y más ligeros. Si lo pensais es algo muy similar al proceso de integración continua distribuida que os comento anteriormente, pero adaptado a un entorno de desarrollo con repositorio centralizado.

Yo veo varias ventajas en la aplicación de un proceso de integración continua distribuida en un entorno de DVCS:

  • El desarrollador pierde el miedo a hacer pull y/o push de otros repositorios al suyo. La integración continua distribuida baja aun más la probabilidad de conflicto por las mismas razones que la IC convencional.
  • Mayor escalabilidad. La integración continua deja de ser un proceso centralizado, ya no hay un servidor centralizado de integración continua, sino una red de procesos distribuida. Podemos tener equipos más grandes trabajando sin necesitar un super servidor.
  • Mayor tolerancia a fallos. Por las mismas razones que antes, no hay dependencia de un servidor central, lo que hace que podamos seguir integrando código.
  • Si el build se rompe, esto es, que no pasa los test, no compila o simplemente no cumple una nivel de calidad mínimo, el resto del equipo puede seguir integrando en otro repositorio mientras se arregla el build. En la integración continua tradicional si el build no pasa, el repositorio central se bloquea para evitar que alguien suba y fusione cambios con una base de código que no funciona. El equipo que había cometido el fallo debe arreglar el build cuanto antes, pero el resto de los desarrolladores ya no pueden seguir integrando. Esto puede generar miedo a subir cambios al repositorio central y también disminuir la frecuencia efectiva de integración. En un sistema distribuido esto no pasa, ya que se puede seguir integrando contra otro repositorio.

Para alcanzar el máximo de estas ventajas hay que montar una topología de repositorios donde no haya repositorios especiales o blessed.

Por último comentaros que hay dos DVCS distintos que pegan fuerte, que son:

  • Git. Escrito en C, es el que tiene más rendimiento y el más flexible de todos. Sin embargo para la gente que viene de SVN puede ser un poco complejo. Destaca que mediante una hash criptográfica, Git, permite detectar si el contenido del repositorio se ha corrompido o ha sido víctima de un ataque malicioso. Se ha usado con éxito para guardar el código del kernel de Linux.
  • Mercurial (Hg). Más sencillo que Git y escrito en Python, es el que tiene de momento, mejor integración con Windows y con herramientas JAVA. No es tan eficiente como Git y no tiene el concepto de ramas. En vez de ello, puedes clonar repositorios, que actúan como ramas. El clonado no implica una copia física del repositorio original, sino que es por referencia. Las operaciones de push y pull actúan por defecto contra el repositorio original del clon.

Conclusión, si queremos ser ágiles la integración continua no nos la quita nadie. De hecho es más importante decidir usar integración continua, que el hecho de elegir entre DVCS o repositorios centralizados no bloqueantes (SVN, CVS…). En cualquier caso a mi esto de los DVCS me parece un paso adelante y algo muy interesante (más flexibilidad, escalabilidad, trabajo offline y adecuar arquitectura de repositorios a la estructura de equipo).

Bueno, espero que os haya sido interesante. Yo me despido por tres o cuatro semanas. Por motivos personales no podré seguir actualizando este blog durante este tiempo, pero no os preocupéis, volveré. Por cierto, os espero por la conferencia de agilismo que se va a producir en Madrid, la CAS2010, donde tengo el honor de dar una charla junto con mi compañero Antonio David, ¡nos vemos y no falteis!

Ups, lo he vuelto ha hacer, más de dos mil palabras…

Read Full Post »