Las ventajas de NO usar inyección de dependencias

La semana pasada escribía en twitter:

Acabo de escribir un constructor que hace new’s de sus dependencias. Ahí, como los antiguos. Tampoco es tan terrible.

Aparte de por trollear discutir un poco y aprender de la gente tan lista que conozco en twitter, realmente lo escribí porque últimamente empiezo a pensar que la inyección de dependencias está sobrevalorada, especialmente eso en lo que se ha convertido la inyección de dependencias en lenguajes como C#. Y si hablamos de los contenedores de inversión de control, aún peor.

Por eso quiero escribir este post, para reflexionar un poco sobre lo que ganamos al no utilizar inyección de dependencias.

That's not clean code

Por supuesto, también perdemos cosas y tenemos que ver cómo de terribles son y si podemos hacer algo al respecto. Como siempre, vamos a intentar analizar esto de forma objetiva y ver hasta dónde llegamos.

Parte de estos argumentos podrían considerarse motivados por un mal uso de la inyección de dependencias, y esto de acuerdo en ello, pero eso no quita que sean problemas frecuentes en bases de código que hacen uso de inyección de dependencias y, por tanto, los considero, cuanto menos, incentivados por ella.

Doy por hecho que todos lo tenemos claro, pero obviamente todo esto depende del contexto, no hay balas de plata, tienes que pensar en la aplicación que estás desarrollando y demás disclaimers habituales.

Lo que ganamos: simplicidad

Todo lo que ganamos gira en torno a un mismo tema: simplicidad. La inyección de dependencias, especialmente la inyección a través de constructor, implica una mayor complejidad que se manifiesta en distintos aspectos.

Por una parte, estamos obligando a los clientes de una clase a saber qué dependencias tiene para poder construirlas. Y no sólo eso. También tiene que conocer las dependencias de sus dependencias. Y las dependencias de las dependencias de sus dependencias… Creo que pilláis la idea.

La inyección de dependencias por contructor tiene un componente viral que hace que pronto sea complicado construir objetos en nuestra aplicación y tengamos que recurrir al uso de contenedores de inversión de control que lo hagan por nosotros. Puesto que ya hablamos de eso hace tiempo, os ahorro la discusión sobre si es bueno o malo usar un contenedor de inversión de control, pero en este caso vamos a quedarnos con que, como poco, incrementa la complejidad de la aplicación.

Además de simplificar la construcción de nuestros objetos, también estamos aumentando el encapsulamiento y la ocultación de información. Al no exponer las dependencias a través del constructor, los clientes de la clase no conocen detalles de la implementación de la clase y el comportamiento queda más encapsulado en ella. Podemos entrar en una discusión semántica sobre si las depedencia se pueden considerar parte del comportamiento y/o de la información de una clase, pero creo que el argumento seguiría siendo válido aunque cambiásemos los nombres.

La contraargumentación típica de esto va ligada a lo que a veces se asocia con el principio de inversión de dependencia. Hay que introducir un interface. Usa inyección de depedencias, pero haz que los clientes no dependan de la clase, sino del interface. Así los clientes no conoces las dependencias de su dependencia y todo queda encapsulado. Por supuesto, para que esto sea manejable, tenemos como antes a nuestro amigo el contenedor de inversión de control. Para ser justos, igual que en el caso anterior, se puede evitar el contenedor y construir el grafo de objetos a mano en algún punto de la aplicación, pero lo habitual suele ser ir por la vía del contenedor.

Esta evolución hacia los interfaces acaba generalmente en otro anti patrón: los header interfaces. Básicamente, acabamos con un montón de interfaces cuya implementación es única en toda la aplicación. Además, esa implementación tiene un API pública que es una copia exacta del interface. Vamos, que aportar, aporta más bien poco, excepto un poco de complejidad adicional al tener un nuevo interface que no sirve para nada, posiblemente otro fichero en disco y hacer algo más incómoda la navegación por el código.

Como decía en la introducción, parte de estos argumentos no pueden achacarse directamente a la inyección de dependencias, sino al abuso, o al mal uso de la misma, pero son escenarios lo bastante frecuentes como para que nos hagan pensar sobre ellos.

Lo que parece que perdemos

Claro, cuando empezamos a utilizar la inyección de dependencias no es que nos hubiéramos vuelto idiotas: tiene sus ventajas. Y, obviamente, esas ventajas son las que perdemos si dejamos de usarlas. Vamos a ver unas cuantas de ellas y si seremos capaces de vivir sin ellas.

Para muchos la ventaja fundamental de la inyección de dependencias es poder escribir tests unitarios. Reconozco que yo fui uno de ellos. Hace unos cuantos años, poder inyectar mocks (o cualquiera de sus amigos) para mi era crítico, porque era lo que me permitía escribir tests unitarios.

Y ojo, que esa es la palabra clave, unitarios. Partía de la base de que los mejores tests son los unitarios, y de que los tests unitarios sólo pueden testear una clase. Con el tiempo mi opinión cambió, ya no creo que un test unitario sólo pueda testear una clase y tampoco creo que los tests unitarios sean siempre la mejor opción. Además, existen formas de escribir el código para poder testear la lógica sin depender de dependencias externas.

Aún así, si quieres escribir tests unitarios de una clase que tiene dependencias y que necesitas testear de forma independiente, siempre puedes hacerlo sin viralizar la aplicación entera con inyección de dependencias y añadir un constructor adicional a esa clase con la o las dependencias que necesitas reemplazar en el test. Algo del estilo de inyección de dependencias para pobres o bastarda. Incluso puedes hacer ese constructor protegido y usarlo sólo desde una clase derivada creada exclusivamente para los tests. Si te pone nervioso tocar el código de producción sólo para poder testearlo, piensa que eso es exactamente lo que estabas haciendo cuando pasaste a usar inyección de dependencias sólo para inyectar stubs.

En cualquier caso, para protegernos de esta eventualidad y poder hacer fácil esta refactorización a un constructor que reciba las dependencias, es recomendable instanciar las dependencias en el constructor, en lugar de repartir los news por toda la clase y luego tener que perseguirlos. Así, en caso de necesitar inyectar esa dependencia, es una refactorización simple para introducir el parámetro en el constructor.

Instanciar todas las dependencias en el constructor nos ayuda a mitigar otro problema: la pérdida de documentación. Cuando usamos inyección de dependencias, es fácil encontrar todas las dependencias de una clase simplemente mirando la signatura del constructor. Al instanciar todas las dependencias en el constructor conseguimos algo parecido. No es igual de descriptivo, pero resuelve el problema en cierta medida.

Esto nos lleva un problema interesante que algunos mecionastéis en twitter (¡gracias!) y que realmente no es fácil resolver sin utilizar inyección de dependencias: el control del ciclo de vida de los objetos.

Cuando instanciamos las dependencias dentro de una clase podemos controlar el ciclo de vida de la dependencia, pero su creación será siempre posterior a la clase que la contiene. Podemos jugar con ligarlos por completo, es decir, instanciar la dependencia en el constructor, asignarla a un atributo de la clase y dejarla ahí hasta que nuestra clase muera. También podemos utilizarla localmente en un método y dejar que muera al terminar el método. Lo que resulta más complicado es hacer que la dependencia sobreviva a nuestra clase (a no ser que la expongamos a alguien que quiera hacerse cargo de ella) y es aún más complicado compartir dependencias entre clases (a alguna hay que inyectársela).

Si no usamos inyección de dependencias y tenemos que compartir dependencias entre clases la solución más habitual es recurrir a singletons, que dista mucho de ser la opción ideal en ese escenario.

Otro argumento a favor de la inyección de dependencias es que nos permite cambiar el comportamiento de un componente sin necesidad de modificarlo, aprovechando que podemos cambiar las dependencias que le inyectamos. Éste es, sin duda, un aspecto muy importante de esta técnica y, si lo aplicamos correctamente, podemos conseguir componentes poco acoplados entre sí, reutilizables en distintos contextos sin necesidad de modificarlos, con responsabilidades bien definidas… en fin, el sueño de todo clean coder.

¿Qué pasa cuando renunciamos a la inyección de dependencias? ¿Perdemos todo eso? En realidad sí. O no. Da igual. En realidad, la pregunta es, ¿necesitamos todo eso? ¿Cuántos sistemas habéis visto en los que se aplica inyección de dependencias, posiblemente con un contenedor, y la configuración es completamente estática? ¿Cuántos sistemas conocéis en los que jamás se modifican las dependencias concretas que recibe cada componente, por mucho que se estén aisladas detrás de un interface? Yo conozco unos cuantos (y he diseñado personalmente muchos de ellos).

Que sea deseable cumplir con esas características, no quiere decir que sea necesario y hay que ser consciente de implica una complejidad adicional que tal vez no merezca la pena. Es similar al caso de abstrear por completo la base de datos “para poder trabajar con diferentes bases de datos” u ocultar el ORM detrás de un montón de interfaces “para poder cambiar de ORM”. ¿Cuántos proyectos necesitan cambiar de ORM o de base de datos? Y en los que así es, ¿realmente es más rentable asumir toda complejidad adicional desde el principio que reescribir la parte que sea necesaria cuando realmente sea necesario?

Donde sí es necesario prestar más atención a estos aspectos es cuando estamos desarrollando un conjunto de componentes cuyo uso escapará a nuestro a control. Si el usuario de nuestros componentes no va a poder modificar su código en el futuro, por ejemplo porque es una librería que vamos a liberar en forma binaria, hacer esa labor de abstracción y flexibilización prematura puede ser importante. Pero siempre buscando el equilibro para que el uso de nuestra librería no imponga una excesiva complejidad en sus clientes a la hora de construir los objetos con los que han de trabajar.

Conclusiones

No quiero convertir esto en un rant contra la inyección de dependencias, ni siquiera contra los contenedores de inversión de control. Lo que pretendo recalcar es que no es práctico utilizar la inyección de dependencias como técnica por defecto para todo. Actualmente parece que new, más que una palabra reservada, es una palabra prohibida, y sigue siendo una forma perfectamente válida de construir un objeto. No todo tiene que ser inyectado por contenedores mágicos.

Cuando trabajo con lenguajes como Javascript, excepto cuando todavía tengo que padecer AngularJS, no utilizo inyección de dependencias. Al menos, en el sentido que se suele asociar con la inyección de dependencias en lenguajes como C#. No uso un contenedor y no empiezo diseñando cada módulo a partir de una función que recibe un puñado de dependencias.

Puedes modularizar una aplicación en componentes de un tamaño razonable, cada uno de ellos lo más autocontenido posible, y utilizar referencias estáticas (import/require) entre ellos. Eso no impide inyectar comportamiento allí donde es necesario, ya sea pasando objetos o funciones a otros objetos o funciones, pero en lugar de ser la norma, es algo que utilizo cuando necesito. No porque sí.

También tengo aplicaciones grandes en las que el uso de inyección de dependencias, con su contenedor de inversión de control, e incluso sus header interfaces, ofrece ventajas importantes. Puedo (y de hecho lo hago) separarlas en varios procesos o ejecutarlas en uno solo. Puedo reutilizar componentes en distintos escenarios, cambiando protocolos de serialización y comunicación de forma transparente. Puedo aprovechar el contenedor y aplicar AOP para determinadas cosas. Aunque tengo que pagar el coste de una complejidad adicional, merece la pena.

Tampoco es necesario que todo un sistema se rija por los mismos principios. Puede haber áreas en las que compense utilizar una técnica, y otras en la que no valga la pena. Podemos tener una aplicación que utiliza inyección de dependencias y un contenedor de inversión de control, pero que para un subsistema determinado emplea una clase que hace fachada y controla todo el subsistema de forma estática.

Lo importante, como ya os podéis imaginar, es mantener la mente abierta y no caer en el error de pensar que siempre compensa hacer las cosas de la misma manera. Estamos muy acostumbrados a hablar sobre eso (balas de platas, martillos y clavos, la herramienta adecuada para cada cosa, etc.), pero a veces nos cuesta llevar esas palabras a la vida real.


14 comentarios en “Las ventajas de NO usar inyección de dependencias

  1. Por dar otro punto de vista voy a romper una lanza en favor de la sobreingenieria :-P , empezando por plantear la forma de analizar la aplicacion de cierta tecnologia en proyectos ya acabados.
    A veces al revisar el proyecto le damos mas importancia al resultado final (que ya no cambia) que a las posibilidades de cambio y las incertidumbres iniciales, que, sin embargo, se pueden repetir en cada proyecto nuevo. A veces parece que nos fijamos en el dolor sordo de baja intensidad de haber elegido abstracciones no usadas en 10 proyectos que en la ausencia de un dolor mayor en uno de ellos por haberlas elegido (ya que en realidad NO lo hemos sufrido). Tambien a veces el no elegirlas provoca consecuencias (y dolores) de forma indirecta y no siempre pensamos “eh, si hubiera elegido no dejar implicitas y fijadas las dependencias globales ahora no estaria copiando y pegando a diestro y siniestro dos años despues”

    En mi caso suelo dejar un constructor abierto con las dependencias en interfaces y de uno a tres constructores mas sencillos con implementaciones por defecto. Segun la generalidad del modulo en cuestion, puede que en alguno solo este el constructor generico y en otros solo el implicito. Intento evitar el contenedor de dependecias para que el añadir una dependencia duela de forma exponencial, si es posible. Simplificar al modulo cliente las dependencias en exceso lleva a añadirlas de una forma mas alegre en el servidor.
    No suelo hacer tests unitarios; en mi caso la razon que me lleva a hacer explicitas las dependencias es intentar evitarlas y conseguir una mayor componibilidad (argh, hasta la palabra es compleja) en los modulos mas abstractos y generales: la cuestion pasa a si necesitas ese nivel de generalidad, claro

  2. Lucas Ontivero dijo:

    Hola Juanma, no es posible estar más de acuerdo. No todo debe poder ser inyectado y aunque creo que nos resultó evidente a todos que lo estábamos haciendo mal, muchas veces nos facilitaba mucho las pruebas unitarias y pensamos en todo lo que haz expuesto en esta entrada.

    Por allá por el 2011 sufrí mucho este problema, y aún hoy cada proyecto que toco sufre de lo mismo, así que lo expuse en una entrada (muy mala por cierto y con un título provocador como de costumbre) en mi blog http://geeks.ms/lontivero/2011/04/15/lo-que-huele-mal-es-la-inyeccin-de-dependencias/

    El punto básico es que si pudiésemos sobrecargar el ‘new’ como en ruby, por ejemplo, nos podríamos olvidar de la inyección de dependencias y vivir en un mundo más feliz.

    Saludos

  3. Olmo del corral dijo:

    Me ha gustado el post. Yo nunca me he tragado lo de la inyección de dependencias. Tiendo a escribir código en métodos estáticos in clases estáticas. Si necesito algo de contexto lo paso explícitamente usando un objeto context o implícitamente usando ThreadLocal/ThreadStatic/CallContext. Y si necesitar puntos de extensión para mockear dependencias simplemente declaró una variable statica:


    static class ReportGenerator{
    static IPrinter Printer;

    static void GenerateReport(ReportData data)
    {
    var pdf = ...
    printer.Print(pdf)
    }
    }

    Al fin y al cabo o estas corriendo los unit test o estas en producción, no necesitas distintas configuraciones para distintas instancias de ReportGenerator al mismo tiempo.

    Si aún queda algún fundamentalista de la orientación a objetos suelto se echara las manos a la cabeza, pero esto es básicamente de lo que van los lenguajes funcionales: métodos estáticos.

  4. Mi querido Juanma :D

    Que gran trabajo llevas a cabo en este blog, me gusta el enfoque “desmelenado” con el que abordas los diversos campos del desarrollo del software. Me gusta cuando leo un nuevo post y en medio me encuentro enlaces a otros post también tuyos donde se habla de algo relacionado o de lo mismo pero escritos por una versión anterior de ti mismo. Incluso en este segundo nivel también se encuentran enlaces a otros post (tercer nivel) donde se pueden ir viendo distintas versiones y enlaces a un cuarto nivel. No es ironía ni es esta una critica negativa, al contrario, la lectura de uno de tus post se puede convertir en algo apasionante digna de una novela del estilo “Elige tu propia aventura” donde puedes saltar a un post de un tema u otro e incluso a distintas versiones de un mismo tema.
    Apasionante para el lector e impresionante el trabajo que lleva para el autor, creo que es el único blog que conozco que no encarcela a su autor. Y es que habitualmente tomamos decisiones, las escribimos y luego nos esclavizamos con nuestras palabras, hay gente que escribe un post en su blog sobre algo y luego escribe código pensando en el post, como si al haberlo hecho público fuera esclavo de sus propias doctrinas. Posiblemente esta sea una de las claves de este blog, escribir sobre experiencias vividas más que de como hay que hacer las cosas o como vas a hacerlas.

    Quería comentar lo anterior para darme pié al verdadero comentario sobre este post. Lo primero que aprendemos son las reglas, las normas, los patrones, el “másmolismo”, leemos en internet artículos donde se habla de una tecnología y “a saber por que” lo tomamos como verdades absolutas, como doctrinas del buen camino. Bajo mi punto de vista, la clave siempre está en aislar nuestro código de lo que leemos, leer los artículos como un aporte a lo que ya sabemos (por supuesto siendo consciente de lo que sabes y de lo que no ;) jeje) y por último ser capaces de extraer que cosas de lo recientemente adquirido merece la pena incorporar en nuestro día a día. Y digo a nuestro “día a día” y no a nuestro “código” por que si bien el código importa, el contexto es más importante (como bien sabes). Como hemos comentado alguna vez mi problema no es escribir código, mi problema es que otros tienen que escribir código. Y nada como los patrones, las reglas y las normas para alinear el código de un equipo de desarrollo. Me resulta más fácil implantar un patrón existente que puede ser conocido por nuevas incorporaciones y que tiene detallado, cual “guía burros”, los aspectos del mismo, que usar ciertas características de cada patrón creando así reglas propias.

    Aún así desde hace un año desarrollo software por mi cuenta, realizando algún proyecto por mi cuenta, y ahí sí, ahí si que voy incorporando cosas según las voy viendo interesantes, y cosas parciales de distintos patrones y “filosofías”.
    En cuanto a DI tengo mi visión particular del tema, no concibo DI sin IoC. Lo veo inútil, supongo que por que no hago tests unitarios. Igualmente al contrario, considero que usar IoC sin DI es tener ganas de joder a quien lea el código e incluso a ti mismo cuando leas tu código dentro de un año, en mi caso dos semanas. Hago uso de IoC en ocasiones muy contadas y cuando lo uso lo que intento es inyectar el DependencyResolver en dicha clase, para que al menos en el constructor de la clase se vea que se usa IoC.
    La gran ventaja de usar DI para mi es que en un simple vistazo puedo ver las dependencias de una clase, dependencias que doy por hecho que va a inyectar el contenedor de IoC. Cierto es que no hago tests unitarios y creo que cada día estoy más lejos de implementarlos por sistema.
    Yo a DI+IoC le veo ventajas por todos sitios, el código habla por si solo viendo los constructores de clase, no le veo ninguna desventaja, quizás me falte rodaje … Lo primero que hago cuando creo una aplicación nueva de ASP.NET MVC ó NancyFx es instalar StructureMap del NuGet, y no entiendo que problema hay con configurar el contenedor de IoC … “La inyección de dependencias por constructor tiene un componente viral” … eso me ha dejado tocado, pero claro luego veo “y tengamos que recurrir al uso de contenedores de inversión de control que lo hagan por nosotros” y claro … yo digo, como si no? hay gente que usa DI sin IoC ??? no lo concebía … pero parece que si. Me parece una locura.

    Bueno, por último (que este comentario ya da para un post en mi blog, jejeje), me gusta cuando aceptas actuar de abogado del diablo y contrarrestas opiniones favorables con opiniones negativas sobre ciertas tecnologías, lo que no me gusta es cuando dices de partir de una idea equivocada, es decir que partes de algo que está mal echo, para enumerar las cosas negativas. Bajo mi punto de vista, cuando critico algo debo de hacerlo desde los términos que ese algo define. Me refiero al párrafo “Parte de estos argumentos podrían considerarse motivados por un mal uso de la inyección de dependencias …” me chirría en la cabeza el echo de quejarme de lo poco que me ha durado la caja de cambios de mi coche cuando no uso correctamente el embrague … no? No se que pensar, pero no me termina de convencer el hecho argumentar cosas negativas de algo que puedan estar motivadas por un mal uso del mismo. Lo digo por que no es la primera vez que te lo veo, con AngularJs te pasa igual, partes de un mal uso o un mal enfoque para enumerar sus cosas negativas.

    Bueno, el sábado que viene en persona ;)
    Un abrazo!

  5. Jose Alonso dijo:

    Hola Juanma,

    Aunque hace tiempo que vengo leyendo tus posts, nunca había comentado ninguno. Pero esta vez tengo que pararme a felicitarte por estos grandes aportes que haces. Le das a tus posts un toque filosófico que parece que no estás escribiendo sobre tecnología.
    Da igual si alguien comparte o no tu visión de las cosas; llegas aquí, lees y acabas aprendiendo algo nuevo, siempre.

  6. Lo siento yo vengo de Java y aun estoy contento con Spring xD, pero es bueno ver el punto de vista y realmente prefiero tener @Resource que un new(…) con muchos parametros.

  7. @Olmo de Corral

    En mi forma de ver la programacion funcional, esta no tiene nada que ver con tener modulos con metodos “estaticos”. De hecho la diferencia entre estatico e instancia no tiene sentido ya que los modulos en haskell, ml, etc no tienen estado ni son tipos en si mismos como en oop. En un lenguaje que no es oop puro (como java o c#) puedes simular los modulos sin datos encapsulados con clases y metodos estaticos

    Los lenguajes procedurales (pascal, c, etc) tambien solo tienen metodos “estaticos” y son imperativos puros, por lo que usarlos en un lenguaje oop manteniendo estado mutable (como las variables estaticas de la clase) es mas una vuelta a c y pascal que nada que tenga que ver con la programacion funcional

    @Javier Ros

    ¿Por que es una locura usar di sin IoC? Yo he usado ambas y cada una tiene sus ventajas e inconvenientes (las ventajas evitarte todas las dependencias del IoC, tener las dependencias en codigo en lugar de en configuracion con lo que tienes disponibles las validaciones del compilador (incluidos parametros de tipos), posibilidad de afinar la inyeccion, rendimiento, etc, etc)
    Las deventajas de no usar IoC creo que las tienes claras ;-)

  8. En favor de los argumentos del Sr. Ros, debo decir que en .NET hoy en día es raro utilizar un IoC con configuración externa. Casi siempre se hace la configuración por código (con convenciones o a mano), con lo que mantienes la seguridad de tipos. Tampoco el rendimiento suele ser problema e incluso hay contenedores que usan LCG para evitar reflection y penalizar aún menos.

    Para mi el mayor inconveniente del contenedor IoC (frente a hacer la inyección a mano) es algo que tu mencionabas en el primer comentario: oculta el dolor.

    Si uso un contenedor IoC, puedo añadir 20 dependencias a una clase y no notaré el problema, serán todas inyectadas por el contenedor.

    Puedo construir grafos de objetos hipercomplejos sin pararme a pensar si realmente tiene sentido partir las funcionalidades así, porque será el contenedor quien construya el grafo.

    También cuesta más ver el acoplamiento entre componentes a través de sus dependencias porque, al no construirlos a mano, no estás tan pendiente de qué acaba recibiendo cada uno.

  9. Si en spring tambien se cambiaron (o añadieron) la configuracion mediante convencion (que tiene sus propios problemas) o con anotaciones o mezclando todo por que la genta acabo harta de xmls y demas.
    No se como es en .net pero si yo tengo un objetos del tipo

    class Bean {
    T getValue()
    }
    class Bean2 {
    public Bean2(Bean bean1) {
    print(bean1.getValue()+1)
    }
    }

    no se si usando IoC para la inyeccion me chequea en tiempo de compilacion que le esta pasando un Bean o me dara un error en ejecucion si le paso un Bean

  10. Los contenedores de .NET son bastante listos con eso (tal vez porque no se hace type erasure) y son capaces de asegurar que los argumentos de tipo (el T) son adecuados, e incluso trabajar con tipos genéricos abiertos y cerrados sin mucho problema.

  11. Hmmm, espera, dices en tiempo de compilación.

    Lo que ningún contenedor (que yo conozca) te garantiza, es que en tiempo de compilación todas las dependencias estén satisfechas. Yo puedo registrar un componente en el contenedor, no registrar una de sus dependencias, y obtendré un error en tiempo de ejecución cuando lo resuelva.

    Hay contenedores (como StructureMap) que tienen herramientas de diagnóstico bastante buenas para detectar eso con tests automatizados, pero en tiempo de compilación no creo que sea posible hacerlo.

  12. Lorenzo Jimenez dijo:

    Durante mis 20 años de programar he visto sin fin de modas, de frameworks, de innovaciones, que a la postre fracasaron pero dieron el camino a avances importantes como por ejemplo las anotaciones en java. Ahora la estoy pariendo con depurar aplicaciones hechas con Backbone y Reactjs pero seguro que dentro de algun tiempo alguien se cansará de lo manual y descubrirá algo que nos beneficie a todos.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>