¿Cómo puedo cuantificar la cantidad de deuda técnica que existe en un proyecto?

67

¿Alguien sabe si hay algún tipo de herramienta para poner un número en la deuda técnica de una base de código, como una especie de métrica de código? Si no, ¿alguien conoce un algoritmo o un conjunto de heurísticas para él?

Si ninguna de esas cosas existe hasta ahora, me interesarían ideas sobre cómo comenzar con tal cosa. Es decir, ¿cómo puedo cuantificar la deuda técnica incurrida por un método, una clase, un espacio de nombres, un ensamblaje, etc.

Estoy más interesado en analizar y evaluar una base de código C #, pero siéntase libre de intervenir también para otros idiomas, especialmente si los conceptos son trascendentes en el lenguaje.

Erik Dietrich
fuente
12
La deuda técnica proviene de decisiones, no de código. Se acumula debido a malas decisiones de gestión. No está claro que "método, una clase, un espacio de nombres, un conjunto" contengan deuda técnica por sí mismos. Representan una responsabilidad cuando hay una mejor opción disponible.
S.Lott
77
Yo diría (en el contexto de la metáfora de la deuda) que los administradores pueden ser los titulares de la deuda, pero los artefactos del código representan la valoración de la deuda y podrían cuantificarse. Es decir, estoy de acuerdo en que los gerentes pueden tomar una decisión como "olvidar las pruebas unitarias porque no tenemos tiempo" y así incurrir en deudas técnicas. Pero, ciertamente creo que puedes poner un número a elementos de código individuales como heurístico. Piénselo de esta manera: si la gerencia toma una serie de decisiones horribles para el futuro, pero no se ha escrito ningún código, ¿hay alguna deuda en ese momento?
Erik Dietrich
3
"¿Hay alguna deuda en ese momento?" La deuda necesita acumularse, tienes razón. Pero no es el código; es el volumen de "trabajo" realizado lo que debe deshacerse. Las especificaciones, los diseños, el código, el trabajo DBA, todo tiene que ser reelaborado. Medir la deuda de los artefactos de software (como las líneas de código fuente) es similar a predecir el costo de desarrollo.
S.Lott
77
Medir la deuda técnica es difícil, además confunde a los gerentes. Sin embargo, puedo decirle una buena manera de combatir la deuda técnica: prototipos baratos, agradables y que funcionen, especialmente si la base del código gira en torno a la GUI. Como Joel sugirió aquí: joelonsoftware.com/articles/fog0000000332.html , dedica un poco de tiempo cada día a limpiar las cosas. El cambio debe ser una mejora positiva, no "OMG, nuestra deuda técnica = pentablobs y está aumentando exponencialmente a un ritmo de ... el cielo está cayendo". Simplemente pase un poco de tiempo cada día en kaizen de una manera que no rompa las cosas que funcionan. Hacer amigos.
Trabajo
66
@ZoranPavlovic A su extraño y extraño dilema no solicitado le falta una tercera opción: quería saber si había alguna herramienta que intentara cuantificar la deuda técnica.
Erik Dietrich

Respuestas:

38

La deuda técnica es solo una idea abstracta que, en algún lugar a lo largo de las líneas de diseño, construcción, prueba y mantenimiento de un sistema, se tomaron ciertas decisiones de manera que el producto se ha vuelto más difícil de probar y mantener. Tener una deuda más técnica significa que será más difícil continuar desarrollando un sistema: o necesita hacer frente a la deuda técnica y asignar más y más tiempo para lo que de otro modo serían tareas simples, o necesita invertir recursos (tiempo y dinero) para reducir la deuda técnica refactorizando el código, mejorando las pruebas, etc.

Hay una serie de métricas que pueden darle alguna indicación sobre la calidad del código:

  • Cobertura de código. Existen varias herramientas que le indican qué porcentaje de sus funciones, declaraciones y líneas están cubiertas por las pruebas unitarias. También puede asignar el sistema y las pruebas de aceptación a los requisitos para determinar el porcentaje de requisitos cubiertos por una prueba a nivel del sistema. La cobertura adecuada depende de la naturaleza de la aplicación.
  • Acoplamiento y cohesión . El código que exhibe bajo acoplamiento y alta cohesión es típicamente más fácil de leer, comprender y probar. Existen herramientas de análisis de código que pueden informar la cantidad de acoplamiento y cohesión en un sistema dado.
  • La complejidad ciclomática es el número de rutas únicas a través de una aplicación. Normalmente se cuenta a nivel de método / función. La complejidad ciclomática está relacionada con la comprensibilidad y la capacidad de prueba de un módulo. Los valores de complejidad ciclomática más altos no solo indican que alguien tendrá más problemas para seguir el código, sino que la complejidad ciclomática también indica el número de casos de prueba necesarios para lograr la cobertura.
  • Las diversas medidas de complejidad de Halstead proporcionan información sobre la legibilidad del código. Estos cuentan los operadores y operandos para determinar el volumen, la dificultad y el esfuerzo. A menudo, esto puede indicar lo difícil que será para alguien recoger el código y comprenderlo, a menudo en casos como una revisión de código o un nuevo desarrollador de la base de código.
  • Cantidad de código duplicado. El código duplicado puede indicar la posibilidad de refactorizar los métodos. Tener un código duplicado significa que hay más líneas para introducir un error y una mayor probabilidad de que existan los mismos defectos en varios lugares. Si existe la misma lógica de negocios en varios lugares, se hace más difícil actualizar el sistema para dar cuenta de los cambios.

A menudo, las herramientas de análisis estático podrán alertarlo de posibles problemas. Por supuesto, el hecho de que una herramienta indique que hay un problema no significa que exista un problema: se necesita juicio humano para determinar si algo podría ser problemático en el futuro. Estas métricas solo le dan advertencias de que podría ser el momento de mirar un sistema o módulo más de cerca.

Sin embargo, estos atributos se centran en el código. No indican fácilmente ninguna deuda técnica en la arquitectura o diseño de su sistema que pueda relacionarse con varios atributos de calidad.

Thomas Owens
fuente
1
Actualmente uso NDepend ( ndepend.com ), CodeRush y métricas de código VS para vigilar las métricas que mencionas (con la excepción de las medidas de Halstead, que analizaré más adelante). Estaba pensando que podría usar alguna combinación de estas métricas para intentar poner algún tipo de número en un elemento de código dado que indicaría, de un vistazo, lo costoso que era para el desarrollo continuo.
Erik Dietrich
@ErikDietrich Puede ser capaz, pero probablemente no cuantificaría ese valor. Quizás sería más apropiado un informe de estilo de "resumen ejecutivo" sobre lo que le dicen sus herramientas métricas, con respecto a los cambios a lo largo del tiempo.
Thomas Owens
2
Otra métrica simple que agregaría a la lista es el número de TODO / HACK / WTF? comentarios en una base de código ...
MaR
@Mar Eso supone que los usa correctamente y no los está jugando para su ventaja. Desea un tiempo extra para limpiar la base del código, solo agregue estos comentarios donde no sean apropiados. No te preocupes por la base de código, simplemente quítala de donde debería estar. Los comentarios pueden mentir, el código no.
Thomas Owens
1
@Thomas Owens: de acuerdo, pero casi cualquier métrica sola puede ser engañada. Si se usa de manera correcta y honesta, la "métrica TODO" proporciona una descripción general barata del código que realmente falta o se debe cambiar (= deuda invisible para métricas basadas solo en código).
MaR
23

Sonar tiene una heurística técnica de la deuda, así como varias otras características útiles para un proyecto de software.

También es compatible con una amplia gama de idiomas.

SonarQube (anteriormente Sonar ) es una plataforma de código abierto para la inspección continua de la calidad del código ...

  • Admite más de 25 idiomas: Java, C / C ++, C #, PHP, Flex, Groovy, JavaScript, Python, PL / SQL, COBOL, etc.
  • SonarQube también se usa en Android Deveopment.
  • Ofrece informes sobre código duplicado, estándares de codificación, pruebas unitarias, cobertura de código, código complejo, posibles errores, comentarios y diseño y arquitectura.
  • Máquina del tiempo y vistas diferenciales.
  • Análisis completamente automatizados: se integra con Maven, Ant, Gradle y herramientas de integración continua (Atlassian Bamboo, Jenkins, Hudson, etc.).
  • Se integra con el entorno de desarrollo de Eclipse.
  • Se integra con herramientas externas: JIRA, Mantis, LDAP, Fortify, etc.
  • Ampliable con el uso de complementos.
  • Implementa la metodología SQALE para calcular la deuda técnica ...
Robert Greiner
fuente
1
¡Genial, gracias! Tengo y uso NDepend para mi trabajo en C #, pero también trabajo un poco en Java y también me interesan las métricas. Por lo menos, esto me da funcionalidad para Java, y puede resultar un buen complemento para NDepend.
Erik Dietrich
Impresionante, usamos Sonar donde trabajo y hace algunas cosas realmente buenas que te dan una idea del estado de tu base de código.
Robert Greiner
2
@ErikDietrich, FYI Sonar también tiene un complemento C # .
Péter Török
@ErikDietrich FYI ahora hay un complemento NDepend para Sonar ndepend.com/docs/sonarqube-integration-ndepend
Patrick Smacchia - NDepend dev
¿Hay alternativas de código abierto?
hellboy
5

Odio usar una analogía de las finanzas, pero parece realmente apropiado. Cuando está valorando algo (activos de cualquier tipo), puede tener un valor intrínseco y extrínseco. En este caso, el código existente tiene un valor intrínseco que sería una cantidad correspondiente a la calidad relativa de dicho código y también tendría un valor extrínseco (valor de lo que podría hacerse al código) y esas cantidades serían aditivas. El valor intrínseco puede desglosarse en créditos y débitos (bueno frente a malo) usando cualquier metodología que esté usando para calificar el código (+5 para comentarios / legibilidad, -10 para cobertura de código, etc.)

Ciertamente no conozco ninguna herramienta que cuantifique esto hoy y creo que tendría una discusión completamente nueva en sus manos si discute los méritos de diferentes estrategias de "valoración de la deuda", pero estoy de acuerdo con Matthew: la deuda es la costo acumulativo de obtener el código tan bueno como sea posible, utilizando cualquier método que use para reducir las horas de trabajo que se necesitan para llegar allí.

Algo más a tener en cuenta es que, sin duda, existe una medida de rentabilidad por la cual, a medida que uno se acerca a la "perfección", es más que probable que el valor de una hora invertida en la base del código disminuya exponencialmente, por lo que probablemente haya un problema de optimización adicional para Maximizar la utilidad del trabajo realizado.

La coincidencia de patrones
fuente
Sí, el concepto de rendimientos marginales decrecientes es ciertamente algo que me gustaría abordar al elaborar y refinar la métrica. Entonces, no solo "aquí está mi argumento objetivo para refactorizar esta clase desde una perspectiva comercial" sino también "aquí está mi justificación para no molestarme en este momento".
Erik Dietrich
5

Entre los desarrolladores, una medida bastante confiable de deuda técnica parece ser WTF / minuto .

El problema con esta "métrica" ​​es que generalmente es bastante difícil comunicarse "fuera".

La métrica que funcionó para mí al comunicar la deuda técnica a los "extraños" fue la cantidad de pruebas y el esfuerzo de corrección de errores (especialmente para corregir errores de regresión ) necesarios para una entrega exitosa.

Una advertencia: aunque este enfoque es bastante poderoso, sería mejor verificarlo con los viejos WTF / minuto antes de recurrir a él. La cosa es que es bastante engorroso: para obtener los datos, uno tiene que rastrear cuidadosamente el tiempo y registrarlo con precisión según las categorías apropiadas.

  • es mucho más fácil decir 3 semanas en total dedicadas a la implementación de la función A que
     
    pasé 14 horas en la implementación preliminar de la función A, luego 29 horas en la prueba de humo, luego 11 horas en la implementación de soluciones para las regresiones que descubrí, luego 18 horas probando el control de calidad Implementación de funciones listas. Después de eso, los chicos de QA pasaron 17 horas probando el lanzamiento inicial del candidato. Después de eso, pasé 13 horas analizando errores enviados por QA para la versión inicial del candidato y 3 horas implementando las correcciones. Después de eso, pasé 11 horas fumando probando los cambios que hice en la liberación inicial de candidatos. Después de esto...

De todos modos, los datos sobre las pruebas y el esfuerzo de corrección de errores han sido bastante fáciles de comunicar en mi experiencia.

Para el lanzamiento reciente, pasamos aproximadamente un 90% de tiempo probando y reparando errores de regresión. Para la próxima versión, sugiera asignar un esfuerzo para reducir este valor al 60-70%.


Otra palabra de precaución. Datos como el 90% anterior podrían interpretarse no solo como una indicación de deuda técnica, sino también (sorpresa sorpresa) como una indicación de que uno no es muy competente en programación / tecnología particular. "Simplemente haces demasiados errores en tu código".

Si existe el riesgo de que los datos se malinterpreten de esa manera, es útil tener datos de referencia adicionales sobre algo menos propenso a WTF para comparar.

  • Digamos que si hay dos componentes / aplicaciones similares mantenidas por el mismo desarrollador (es), primero lanzando a "tasa de desperdicio" aproximadamente 50% y segundo a 80-90, esto es un caso bastante fuerte a favor de que el segundo sea ​​sujeto de deuda técnica.

Si hay probadores dedicados en el proyecto, también podrían contribuir a una evaluación más objetiva de los datos. Como mencioné en otra respuesta ,

Con los probadores, usted consigue que alguien respalde su comprensión de los problemas de diseño. Cuando solo hay desarrolladores quejándose de la calidad del código , esto a menudo suena como WTF subjetivos detrás de la puerta cerrada .
 
Pero cuando esto se repite por un tipo de control de calidad que dice algo así como component A100 errores de regresión para 10 nuevas funciones, en lugar de component B10 errores de regresión por 20 nuevas funciones , la comunicación de repente se convierte en otro juego.

mosquito
fuente
2
Me gusta mucho esta respuesta. Con un departamento de control de calidad dedicado, la relación de defectos de regresión a defectos nuevos es muy sencilla de calcular y definitivamente podría decirle mucho sobre la deuda técnica.
Erik Dietrich
4

Creo que la pregunta es cuánto costaría "recomprar" su deuda técnica, es decir, ¿cuánto trabajo cuesta arreglarla? Bueno, depende del equipo averiguarlo.

Durante la planificación del sprint, le pido al equipo que calcule la complejidad de arreglar los elementos de la deuda técnica de la misma manera que estimarían la complejidad de una historia de usuario. En ese momento, es un juego de negociación entre el equipo y el propietario del producto para determinar qué deuda técnica es una prioridad lo suficientemente alta como para hacerse en el sprint actual (desplazando historias de usuarios reales) y qué puede esperar.

Si no está haciendo scrum, me apegaría a mi premisa: la deuda técnica debe medirse por el costo del remedio.

Matthew Flynn
fuente
Entonces, en el contexto de los puntos de la historia, ¿es justo decir que podría agregar algunos puntos a cada historia si hubiera un alto grado de deuda técnica representada por las áreas afectadas del código? Es decir, si la historia X implica agregar al elemento de código Y, que es simplemente horrible, ¿agrega algunos puntos a la historia específicamente debido a la naturaleza de Y? ¿Y ese número de puntos es el mismo o está relacionado con el número de puntos para realizar la corrección que mencionó que estima?
Erik Dietrich
1
@Erik Dietrich - Bueno, el TD definitivamente agrega complejidad a la solución. La dificultad puede ser que arreglar TD poco a poco puede ser más costoso que una solución mayorista. Por lo tanto, es posible que tenga 3 historias que serían calificadas en 5 cada una si se eliminara el TD, pero son 8 cada una con la deuda en su lugar, lo que suma 9 puntos de TD. La tarea de arreglar el TD como un todo (independientemente de las historias) en realidad puede ser un 8. Por lo tanto, puede argumentar que la solución mayorista cuesta menos (8) que la fragmentaria (9). Esto sería parte de la negociación
Matthew Flynn
Eso tiene sentido. Y, ciertamente, a lo que estoy tratando de llegar es a hacer un caso (algo) objetivo para decir algo así como "en un año, podemos desarrollar X nuevas características si seguimos avanzando, pero X + Y nuevas características si pagar parte de esta deuda técnica ".
Erik Dietrich
2

Hay una plataforma bastante fuerte por ahí llamada CASTbuscar deuda técnica en grandes aplicaciones. Lo usamos en un proyecto donde asumimos una gran mejora de un sistema heredado. No le dice qué estaba en la cabeza de las personas que escribieron el código, pero examina el código y encuentra fallas en el código y la arquitectura, luego cuantifica la deuda técnica si lo desea. Sin embargo, el uso real al mirar esto no es el monto en dólares sino la lista de problemas que ya están en el código. Esto le informa sobre una parte de la deuda técnica que tiene (por lo que no estoy de acuerdo con algunas de las respuestas anteriores). Hay una deuda técnica que se basa puramente en el diseño y que es muy subjetiva, como la pornografía, lo sabes cuando lo ves y conoces el contexto. Yo diría si eso es realmente una deuda "técnica". Hay una deuda técnica que está puramente en la implementación y creo que '

Leonhard
fuente
Compartí esta pregunta en Twitter y alguien respondió hablando de CAST. No tengo muy claro qué hace después de visitar su sitio web. ¿Hay alguna versión gratuita o de demostración para probarlo, por casualidad?
Erik Dietrich
2

Aquí hay un seminario web del MIT que describe la investigación sobre la deuda técnica en grandes sistemas de software: http://sdm.mit.edu/news/news_articles/webinar_050613/sturtevant-webinar-technical-debt.html

Los autores escribieron código para analizar un proyecto y extraer métricas de 'complejidad arquitectónica'. Se demostró que estas métricas tienen una fuerte relación con la densidad de defectos, la productividad del desarrollador y la rotación del personal de desarrollo.

El trabajo descrito en el seminario web se basa en la investigación de modularidad realizada por Alan MacCormack y Carliss Baldwin en la Harvard Business School. También miraría sus papeles. Su 'costo de propagación' podría ser lo que está buscando.

usuario94203
fuente
1

Diría que las métricas de código estándar se pueden usar como una vista relativa de alto nivel del endeudamiento técnico. VS Ultimate incluye un analizador de código que le proporcionará un "índice de mantenimiento" basado en la complejidad ciclomática, el acoplamiento, la LoC y la profundidad de la herencia. Puede sumergirse en cualquier punto problemático y ver detalles (hasta el nivel de función). Acabo de ejecutarlo en mi proyecto y los puntajes más bajos que obtuvimos fueron 69 en nuestro paquete de datos (configuración e inicialización de EF) y nuestro conjunto de pruebas. Todo lo demás era de 90 o más. Hay otras herramientas que le darán más métricas como las discutidas en el PPP del tío Bob

Michael Brown
fuente
Entonces, digamos que tenía algo que no estaba en el paquete de prueba o en el paquete de datos con un puntaje inferior a 90. ¿Tiene un umbral numérico allí donde dice: "está bien, eso no es lo suficientemente bueno y vamos a refactorizar"? ¿O utiliza esta información para exponer ante la gerencia o algún interesado que es necesaria una refactorización? Es decir, ¿los administradores / partes interesadas se preocupan por el índice de mantenibilidad de Microsoft, o presentan esa información de alguna otra manera? ¿O simplemente no lo presenta y soluciona el problema en silencio por su cuenta?
Erik Dietrich
Me encanta esa pregunta Mi respuesta siempre será que escribir el mejor código que puedas no es algo que pidas permiso para hacer. Uso lo que el tío Bob llama la "regla boyscout" (siempre dejo el código en mejores condiciones que cuando llegó) y llamo refactorización oportunista. La idea es que cuando tenga que modificar el código existente, tómese el tiempo para a) cubrirlo en las pruebas unitarias b) refactorizarlo para que esté más limpio. Michael Feathers, que trabaja eficazmente con Legacy Code, proporciona alguna guía para hacerlo.
Michael Brown
@ Mike-That lo despedirá en muchos entornos de desarrollo donde se realiza un seguimiento y control estricto de todos los cambios de código. Especialmente si su mejora aparentemente inocente que nadie le dijo que corrigiera terminó rompiendo algo que alguna vez funcionó.
Dunk
Tenga en cuenta que no dije sumergirse y willy-nilly limpiar el código. Le dije que limpiara el código en el que ya está trabajando. También he trabajado con código altamente regulado (se me asigna un elemento de trabajo, tengo que proporcionar una lista de los cambios que se realizan para abordar el elemento de trabajo para su aprobación, realizar cambios aprobados ) 9/10 veces explicando la refactorización en la solicitud de cambio daría lugar a la aprobación.
Michael Brown
0

No pensaría en la deuda técnica como dólares donde se necesita un modelo elegante para cuantificarla. Lo consideraría como favores. Si alguien te hace un favor y es probable que lo olvides, escríbelo. Cuando tomes un atajo, escríbelo. Esto te ayuda a recordar, y más impotente te obliga a reconocerlo. No se necesita ninguna herramienta sofisticada. Bloc de notas o Ecxel pueden hacer el truco.

MathAttack
fuente
2
Desde una perspectiva realpolitik, diría que las personas más dispuestas a incurrir en maldad a largo plazo por resultados a corto plazo son probablemente también las personas menos propensas a documentar sus decisiones. Por lo tanto, estoy de acuerdo con su idea en teoría, pero creo que los "solicitantes de favor" en serie serían los menos propensos a realizar un seguimiento del equilibrio de favores.
Erik Dietrich
@ErikDietrich - Estoy de acuerdo. Y los peores infractores en serie ni siquiera saben que están aumentando su deuda. (Similar a los peores infractores de tarjetas de crédito que aplastan sus calificaciones crediticias). Pero el punto de partida supone el deseo de cuantificar, y es difícil para el no escritor cuantificarlo. No sabes dónde está la caca, a menos que sea tu perro, o que hayas pisado.
MathAttack
0

Trabajo para una empresa que está investigando esto exactamente. A continuación se presentan 3 métricas accionables que recomendamos observar al abordar la deuda técnica. Para obtener más información sobre "cómo" y "cuándo" rastrearlos, reunimos un resumen del artículo 3 Métricas para comprender y abordar la deuda técnica .

¿Cuáles son tus pensamientos? Feliz de responder cualquier pregunta y ansioso por escuchar sus comentarios :).

Propiedad para evitar defectos y deudas tecnológicas no deseadas

La propiedad es un indicador principal de la salud de la ingeniería.

Las partes de la base de código que reciben contribuciones de muchas personas se acumulan con el tiempo, mientras que las que reciben contribuciones de menos personas tienden a estar en un mejor estado. Es más fácil mantener altos estándares en un grupo ajustado que está bien informado sobre su parte de la base de código.

Esto proporciona cierto poder predictivo: es probable que partes de la base de código de propiedad débil acumulen deudas con el tiempo y sean cada vez más difíciles de trabajar. En particular, es probable que la deuda se asuma involuntariamente , simplemente como un efecto secundario de la información incompleta y la propiedad diluida de la calidad del código.

Esto es algo análogo a la tragedia de los bienes comunes .

Cohesión para mejorar la arquitectura.

La cohesión es un indicador final de componentes bien definidos.

La cohesión y su contraparte, el acoplamiento, han sido reconocidos durante mucho tiempo como conceptos importantes en los que centrarse al diseñar software.

Se dice que el código tiene una alta cohesión cuando la mayoría de sus elementos van de la mano. La alta cohesión generalmente es preferible porque está asociada con la mantenibilidad, la reutilización y la robustez. La alta cohesión y el acoplamiento flojo tienden a ir de la mano.

Más allá de estar asociado con un código más reutilizable y mantenible, la alta cohesión también minimiza la cantidad de personas que necesitan participar para modificar una parte determinada de la base de código, lo que aumenta la productividad.

Batir para identificar áreas problemáticas

La rotación (actividad repetida) ayuda a identificar y clasificar las áreas maduras para refactorizar en un sistema en crecimiento.

A medida que los sistemas crecen, se hace más difícil para los desarrolladores comprender su arquitectura. Si los desarrolladores tienen que modificar muchas partes de la base de código para ofrecer una nueva característica, será difícil para ellos evitar la introducción de efectos secundarios que conduzcan a errores, y serán menos productivos porque necesitan familiarizarse con más elementos y conceptos.

Es por eso que es importante luchar por la responsabilidad única de crear un sistema más estable y evitar consecuencias no deseadas. Si bien algunos archivos son centros arquitectónicos y permanecen activos a medida que se agregan nuevas funciones, es una buena idea escribir código de una manera que cierre los archivos y revise, pruebe y controle rigurosamente las áreas de QA.

Churn muestra estos archivos activos para que pueda decidir si se deben desglosar para reducir la superficie de cambio en su base de código.

Stefanie
fuente
-1

Si tiene un buen historial a través de un rastreador de errores o algún tipo de software ágil, puede mantenerlo simple. Tiempo dedicado a completar tareas básicas. Además, la fiabilidad de las estimaciones cuando el proyecto era joven frente a ahora.

Erik Reppen
fuente