¿Cuándo es un GRAN reescribir la respuesta?


278

Acabo de leer la pregunta sobre las grandes reescrituras y recordé una pregunta que quería responder.

Tengo un proyecto horrible que se me transmitió, escrito en Java antiguo, usando Struts 1.0, tablas con relaciones inconsistentes, o ninguna relación en absoluto e incluso tablas sin claves primarias o campos destinados a ser claves primarias, pero no son únicos en absoluto. De alguna manera, la mayor parte de la aplicación "simplemente funciona". La mayoría de las páginas se reutilizan (copia el código pegado) y están codificadas. Todos los que han trabajado en el proyecto lo han maldecido de una forma u otra.

Ahora había considerado durante mucho tiempo proponer a la alta gerencia una reescritura total de esta aplicación horrenda. Poco a poco intento hacerlo en tiempo personal, pero realmente siento que esto merece algunos recursos dedicados para que esto suceda. Después de leer los artículos sobre grandes reescrituras, tengo dudas. Y eso no es bueno cuando quiero convencer a mis superiores de apoyar mi reescritura. (Trabajo en una empresa bastante pequeña, por lo que la propuesta tiene la posibilidad de ser aprobada)

TL; DR ¿Cuándo es una gran reescritura de la respuesta y qué argumentos puede usar para apoyarla?



66
Es viejo, pero es un clásico: Joel's "Cosas que nunca debes hacer, Parte I" joelonsoftware.com/articles/fog0000000069.html
Mawg

Esta es una gran pregunta. Muy relevante, esta situación ocurre con frecuencia en ingeniería de software.
Brad Thomas

Respuestas:


325

Lo sentimos, esto va a ser largo, pero se basa en la experiencia personal como arquitecto y desarrollador en múltiples proyectos de reescritura.

Las siguientes condiciones deberían hacer que considere algún tipo de reescritura. Hablaré sobre cómo decidir cuál hacer después de eso.

  • El tiempo de aceleración del desarrollador es muy alto. Si se necesita más tiempo que por debajo (por nivel de experiencia) para aumentar un nuevo desarrollador, entonces el sistema debe ser rediseñado. Por tiempo de aceleración, me refiero a la cantidad de tiempo antes de que el nuevo desarrollador esté listo para realizar su primer compromiso (en una función pequeña)
    • Recién salido de la universidad - 1.5 meses
    • Todavía verde, pero he trabajado en otros proyectos antes - 1 mes
    • Nivel medio - 2 semanas
    • Experimentado - 1 semana
    • Nivel sénior - 1 día
  • La implementación no se puede automatizar debido a la complejidad de la arquitectura existente.
  • Incluso las correcciones de errores simples toman demasiado tiempo debido a la complejidad del código existente
  • Las nuevas funciones tardan demasiado y cuestan demasiado debido a la interdependencia de la base de código (las nuevas funciones no pueden aislarse y, por lo tanto, afectan las funciones existentes)
  • El ciclo de prueba formal lleva demasiado tiempo debido a la interdependencia de la base de código existente.
  • Se ejecutan demasiados casos de uso en muy pocas pantallas. Esto causa problemas de capacitación para los usuarios y desarrolladores.
  • La tecnología que tiene el sistema actual lo exige
    • Los desarrolladores de calidad con experiencia en la tecnología son demasiado difíciles de encontrar.
    • Está en desuso (no se puede actualizar para admitir plataformas / características más nuevas)
    • Simplemente hay una tecnología de nivel superior mucho más expresiva disponible
    • El costo de mantener la infraestructura de la tecnología anterior es demasiado alto.

Estas cosas son bastante evidentes. Cuándo decidir sobre una reescritura completa versus una reconstrucción incremental es más subjetivo y, por lo tanto, tiene más carga política. Lo que puedo decir con convicción es que afirmar categóricamente que nunca es una buena idea está mal.

Si un sistema puede rediseñarse de forma incremental y tiene el pleno apoyo del patrocinio del proyecto para tal cosa, entonces debe hacerlo. Aquí está el problema, sin embargo. Muchos sistemas no pueden ser rediseñados de forma incremental. Estas son algunas de las razones que he encontrado para evitar esto (tanto técnico como político).

  • Técnico
    • El acoplamiento de componentes es tan alto que los cambios en un solo componente no pueden aislarse de otros componentes. Un rediseño de un solo componente da como resultado una cascada de cambios no solo en los componentes adyacentes, sino indirectamente en todos los componentes.
    • La pila de tecnología es tan complicada que el diseño de estado futuro requiere múltiples cambios de infraestructura. Esto también sería necesario en una reescritura completa, pero si se requiere en un rediseño incremental, entonces pierde esa ventaja.
    • Rediseñar un componente da como resultado una reescritura completa de ese componente de todos modos, porque el diseño existente es tan fubar que no hay nada que valga la pena guardar. Nuevamente, pierde la ventaja si este es el caso.
  • Político
    • No se puede hacer que los patrocinadores comprendan que un rediseño incremental requiere un compromiso a largo plazo con el proyecto. Inevitablemente, la mayoría de las organizaciones pierden el apetito por el drenaje continuo del presupuesto que crea un rediseño incremental. Esta pérdida de apetito también es inevitable para una reescritura, pero los patrocinadores estarán más inclinados a continuar, porque no quieren dividirse entre un sistema nuevo parcialmente completo y un sistema antiguo parcialmente obsoleto.
    • Los usuarios del sistema están demasiado apegados a sus "pantallas actuales". Si este es el caso, no tendrá la licencia para mejorar una parte vital del sistema (el front-end). Un rediseño le permite evitar este problema, ya que están comenzando con algo nuevo. Todavía insistirán en obtener "las mismas pantallas", pero tienes un poco más de munición para hacer retroceder.

Tenga en cuenta que el costo total de rediseñar de forma incremental siempre es mayor que hacer una reescritura completa, pero el impacto para la organización suele ser menor. En mi opinión, si puede justificar una reescritura y tiene desarrolladores superestrellas, hágalo.

Solo hágalo si puede estar seguro de que existe la voluntad política de completarlo. Esto significa la aceptación tanto del ejecutivo como del usuario final. Sin ella, fracasarás. Supongo que es por eso que Joel dice que es una mala idea. La aceptación de ejecutivos y usuarios finales parece un unicornio de dos cabezas para muchos arquitectos. Tienes que venderlo agresivamente y hacer campaña para que continúe continuamente hasta que se complete. Eso es difícil, y estás hablando de apostar tu reputación en algo que algunos no querrán ver tener éxito.

Algunas estrategias para el éxito:

  • Sin embargo, si lo hace , no intente convertir el código existente. Diseñe el sistema desde cero. De lo contrario, estás perdiendo el tiempo. Nunca he visto ni oído hablar de un proyecto de "conversión" que no haya terminado miserablemente.
  • Migre los usuarios al nuevo sistema un equipo a la vez. Identifique los equipos que tienen el MÁS DOLOR con el sistema existente y migre primero. Permítales difundir las buenas noticias de boca en boca. De esta manera, su nuevo sistema se venderá desde adentro.
  • Diseñe su marco como lo necesite. No comience con un marco de trabajo que he pasado 6 meses construyendo este marco que nunca ha visto código real.
  • Mantenga su pila de tecnología lo más pequeña posible. No sobre diseño. Puede agregar tecnologías según sea necesario, pero eliminarlas es difícil. Además, cuantas más capas tenga, más trabajo les costará a los desarrolladores hacer cosas. No lo dificultes desde el primer momento.
  • Involucre a los usuarios directamente en el proceso de diseño, pero no permita que dicten cómo hacerlo. Gana su confianza mostrándoles que puedes darles lo que quieren mejor si sigues buenos principios de diseño.

25
Creo que el punto de Joel es que al hacer una reescritura, pierdes el conocimiento acumulado en el código anterior.
quant_dev

14
@quant_dev en parte sí, pero cuando reescribes a veces te das cuenta de que muchos de los errores en el sistema anterior se debieron a la forma en que funcionaba el sistema anterior, no estrictamente a la lógica de cómo debería funcionar el sistema ideal.
Tjaart

13
Joel dice que fallará porque, durante la reescritura, no agrega ninguna funcionalidad nueva a su aplicación. El único propósito de una reescritura es eliminar parte o la totalidad de la deuda técnica. Por supuesto, esto no es poca cosa, pero es arriesgado si sus competidores están agregando nuevas características y funcionalidades a su software mientras usted simplemente se está deshaciendo de las deudas técnicas que tiene.
Robert Harvey

25
He trabajado con una base de código que se ajusta a todos estos criterios y, sin embargo, una Gran Reescritura todavía falló y, de alguna manera, empeoró las cosas al darnos un código de "nuevo acuerdo" medio implementado para manejar que era muy diferente del viejo caos pero no Mucho más manejable. OMI, solo si se vuelve completamente imposible refactorizar, debe eliminar todo el enfoque de agregar funciones y corregir errores que le están costando a sus clientes hoy. A menos que esté hablando de un código verdaderamente indescifrable, un equipo que no puede extraer bits modulares para una reutilización más fácil a medida que avanza, probablemente no puede rediseñar lo suficientemente bien para la gran rw de todos modos.
Erik Reppen

11
Esta es una respuesta útil, pero lejos de ser fantástica, porque da una impresión equivocada en algunas cosas. Por ejemplo, "Tenga en cuenta que el costo total de rediseñar incrementalmente siempre es mayor que hacer una reescritura completa" : la palabra "siempre" en esa oración es incorrecta, porque "rediseñar incrementalmente" le brinda la oportunidad de reutilizar al menos algunas partes del diseño existente, mientras que una "reescritura completa" no le ofrece eso. Sé que con seguridad, porque realmente había estado en esa situación, donde reescribimos una aplicación> 100K LOC parcialmente en su totalidad, en parte no.
Doc Brown

109

He participado en dos grandes reescrituras. Primero fue un pequeño proyecto. El segundo fue el producto principal de una empresa de software.

Hay varias trampas:

  • Las reescrituras siempre tardan más de lo esperado.
  • Las reescrituras no tienen efectos / beneficios directos para el cliente.
  • La capacidad dedicada a la reescritura no se utiliza para apoyar al cliente.
  • perderá funcionalidad con una reescritura a menos que tenga documentación al 100%.

Las reescrituras rara vez son la respuesta real. Puede refactorizar gran parte del código sin perder nada y sin mucho riesgo.

Las reescrituras pueden ser la respuesta si:

  • Estás cambiando a otro idioma o plataforma.
  • Está cambiando marcos / componentes externos.
  • el código base existente ya no se puede mantener.

Pero recomiendo encarecidamente el enfoque lento mediante la refactorización. Es menos riesgoso y mantiene contentos a sus clientes.


11
+1 para el enfoque refactorizado. Muchas veces no hay suficiente tiempo o horas de trabajo para reescribir Y mantener el sistema existente.
Ryan Hayes

Esto se usa actualmente para una aplicación de demostración (ningún cliente la ha comprado recientemente y pensé que ahora es el mejor momento para recrearla).
Jonn

44
@John: Como ejecutivo, me resultaría muy difícil aprobar la reescritura de una aplicación para la que mi equipo de ventas aún no tiene un cliente. Con toda honestidad, le daría una cierta cantidad de tiempo y luego decidiría qué hacer. Si no hay interés, lo arruinaría todo y elegiría algo más que hacer.
NotMe

44
Recientemente reescribí una aplicación de Visual Basic en Java. Esto permitió que se ejecutara como un servicio en Windows (sin Java GUI): beneficio para el cliente.

44
"las reescrituras no tienen efectos / beneficios directos para el cliente"; esto es a menudo un mito, ya que los marcos más nuevos proporcionan "incorporadas" muchas mejoras de productividad que son imposibles o demasiado caras de implementar. Un ejemplo, actualizar una aplicación vb6 a una aplicación .net permite a los usuarios abrir archivos más grandes (debido a una arquitectura de 64 bits), lo que significa que los usuarios finales no tienen que interrumpir artificialmente su trabajo.
Stephen

72

Es hora de reescribir cuando:

El costo de reescribir la aplicación + mantener la aplicación reescrita es menor que el costo de mantener el sistema actual a lo largo del tiempo.

Algunos factores que hacen que mantener el actual sea más costoso:

  • El idioma es tan antiguo que tiene que pagarle a las personas que saben mucho dinero programarlo (COBOL).
  • (por experiencia, desafortunadamente) El sistema está en una arquitectura de hardware que es tan antigua que tienen que buscar en Ebay y RECOPILAR partes para agregarlas a la máquina en la que se está ejecutando porque ya no están hechas. Esto se llama "soporte vital de hardware" y es costoso porque a medida que las piezas se vuelven más escasas, (pueden) subir de precio o (absolutamente) eventualmente se agotarán.
  • Se ha vuelto tan complejo que el //Here be dragons.comentario está en todo su código.
  • No puede escribir ningún otro proyecto y agregar un nuevo valor a la empresa porque siempre está remendando a esta bestia fea.

Eso es exactamente lo que provocó la reescritura en la que participé. El código era tan frágil y el costo de agregar nuevas funciones era tan alto que no reescribir ya no era una opción.
Frank Shearar

16
Estoy riendo aquí sobre el punto # 2.
Paul Nathan

44
@Paul: Yo también lo hice cuando un cliente me dijo eso, luego descubrí que hablaban en serio ... y que estaría haciendo requisitos para la reescritura. Qué día tan emocionante fue ese.
Ryan Hayes

3
El problema es cómo mides los costos.

2
Me gusta esta respuesta, divertida y verdadera. Me gustaría agregar "cuantificablemente" justo antes de "menos". Muchas veces, es fácil hacer el reclamo, pero es importante poder cuantificar el tiempo perdido.
Kevin Hsu

17

Si necesita un cambio fundamental en la arquitectura del proyecto, posiblemente sea hora de comenzar de nuevo.

Incluso con eso, potencialmente habrá grandes extensiones de código que aún vale la pena reutilizar en su nuevo proyecto.

Sin embargo, tenga en cuenta la advertencia justa. Un proyecto actual tendrá sus reglas comerciales probadas y refinadas con innumerables horas de uso real, algo que no será cierto para un proyecto que se inició desde cero.

Dudo que un período de tiempo o una intuición sea una medida apropiada de una medida tan drástica. Debe tener razones claras , defendibles y bien entendidas para participar en este curso de acción.


3
Debe tener mucho cuidado al 'volver a asignar grandes extensiones de código', esto puede conducir a un código heredado no utilizado y una estructura de código deficiente. Refactorizar es una mejor solución en mi opinión.
mrwooster

1
Convenido. Parte de esa declaración debe interpretarse como "refactor" en su "nuevo" proyecto. Es decir, si ya se ha comprometido en ese camino drástico. Como lo mencionaron otros, esta es una rareza extrema que casi nunca debería hacerse.
Dan McGrath el

1
+1. Además, la reescritura llevará tiempo, durante el cual deberá realizarse el mantenimiento, pero deberán realizarse los mismos cambios en ambas bases de código.
Larry Coleman

12

Aunque estoy de acuerdo con la respuesta de Kramii y la opinión de Joel, hay momentos en que es apropiado hacer una reescritura. En aplicaciones de larga duración (estoy hablando de 10 a 20 años o más), el mantenimiento se vuelve cada vez más costoso con el tiempo. Esto se debe a que el código se está volviendo cada vez más espagueti ya que la arquitectura original se sacrifica por parches de mantenimiento rápidos. Además, los desarrolladores de tecnologías más antiguas se vuelven más raros y más caros. Finalmente, el hardware comienza a envejecer y cada vez es más difícil encontrar nuevo hardware, sistemas operativos, marcos, etc. para ejecutar la aplicación anterior. Además, las empresas evolucionan, y lo más probable es que un sistema anterior no satisfaga las necesidades comerciales de la organización como lo haría un sistema nuevo.

Por lo tanto, debe sopesar todos los costos de mantenimiento continuo, así como los beneficios potenciales de un nuevo sistema para el negocio, contra el costo de reescribirlo desde cero. Sea muy pesimista en sus estimaciones sobre el costo de una reescritura. Casi siempre cuesta más y lleva más tiempo de lo que piensas.


+1 por mencionar la Ley de Hofstadter .
Baelnorn

11

¡Detener! Reescribir casi nunca es la respuesta. La mayoría de las veces, refactorizar es una mejor apuesta.


Por supuesto, no son momentos en los que una reescritura se justifica:

  • Estás cambiando a una nueva plataforma donde las herramientas de migración no existen (o no se pueden escribir a un precio suficientemente bajo).
  • La aplicación a reescribir es trivial.
  • El código fuente de la aplicación original se pierde y la recuperación es más costosa que la reescritura.
  • La gran mayoría de las reglas comerciales encapsuladas por la aplicación existente ya no se aplican.
  • Hay pocos usuarios activos del código existente.
  • Tienes el recurso (tiempo, talento y herramientas) para emprender la reescritura.
  • La aplicación existente no se puede ejecutar en un entorno de producción, por razones legales o prácticas.

Para entender por qué recomiendo refactorizar sobre reescribir, considere lo que implica una reescritura. Debes:

  1. Comprenda los matices de lo que hace la aplicación existente. Esto no suele ser trivial cuando se tienen en cuenta todas las reglas comerciales sutiles que encapsula, el entorno (tanto humano como técnico) en el que opera y las ventajas y desventajas de la solución actual. La mayoría de las veces, el único lugar donde existe esta información (si está en algún lugar) es en el código fuente de la aplicación existente. Es lamentable que una de las principales razones para realizar una reescritura es que el código existente es difícil de entender y mantener.

  2. Reproduzca esta funcionalidad (o una versión actualizada de la misma) en una nueva aplicación probada y confiable. Es posible que los desarrolladores no entiendan el código existente, pero sus usuarios lo entienden bien. Es posible que no satisfaga sus necesidades comerciales actuales, pero al menos pueden decirle qué hace la aplicación en diversas circunstancias.

Las grandes ventajas de la refactorización son que:

  • Puedes tomar las cosas una pieza pequeña a la vez.
  • Cualquier cambio puede probarse en el contexto de la aplicación de trabajo existente.
  • Las hipótesis sobre la forma en que funciona el código existente se pueden probar haciendo pequeños cambios y observando lo que sucede.
  • Los cambios a menudo se pueden entregar a los usuarios en fases en lugar de todos a la vez.
  • Aprender de las primeras etapas de la refactorización puede informar las etapas posteriores de la refactorización.
  • Si abandona el proceso a mitad de camino, seguirá habiendo beneficios en términos de una base de código más limpia (en lugar de una reescritura que debe completarse para ofrecer cualquier beneficio al usuario o desarrolladores).

Recuerde también que si hace una reescritura, tiene la garantía de introducir muchos errores nuevos y meterse en la nueva base de código.


2
¿Podría ampliar esta respuesta explicando por qué refactorizar con más frecuencia es mejor que reescribir? ¿Y cuándo sería una reescritura la respuesta?
gablin

Dado el hecho de que una reescritura con mayor frecuencia es el mismo desastre en un atuendo diferente, tienes razón. Si puede eliminar ese único problema, entonces está equivocado. No hay absolutos cuando las personas adecuadas lo hacen.
JensG

10

Este gráfico podría ayudar, es una función de la calidad de la base del código y el valor comercial de la aplicación:

ingrese la descripción de la imagen aquí

El cuadro pretende ser una guía sobre cuándo se justifica una reingeniería del software heredado y cuándo no. Por ejemplo, si el software tiene un alto valor comercial y la calidad del código es deficiente, se justifica una reingeniería.


44
¿Por qué invertirías en reescribir un proyecto con bajo valor comercial? ¡Solo deséchalo!
Jørgen Fogh

Es por eso que dice "reemplazar o ...". Puedes reemplazarlo por algo que tenga valor. O conviértalo en algo que tenga valor. Pero tienes un punto. Lo editaré para agregar la opción "desecharlo".
Tulains Córdova

8

Creo que estoy en la única situación en mi carrera donde la gran reescritura es la respuesta:

Fusión de empresas, gran superposición en la funcionalidad de los sistemas. Muchos, muchos sistemas se han fusionado y retirado, y otros aún están en proceso.

Heredé un sistema de la otra compañía que aún vive. Tiene una base de código enorme, que solía soportar múltiples departamentos muy similares a los nuestros, pero con un diseño y marcos completamente diferentes. Solo queda un sector empresarial que lo usa, lo que genera suficiente dinero para mantener esta cosa viva en un estado zombie. Toda la experiencia anterior se ha ido, no hay documentación. La carga de soporte es alta, con fallas cada semana. No se ha fusionado con los sistemas de nuestras empresas, porque nuestra empresa nunca apoyó este sector particular del negocio, por lo que no tenemos la funcionalidad o la experiencia.

Parece que este es el único caso donde se necesita la reescritura. Parece que voy a tener que descubrir este gigante, extraer los bits de funcionalidad dedicados a este negocio y reescribir las piezas que deben agregarse a nuestros sistemas existentes. Una vez hecho esto, nuestros sistemas existentes pueden soportar este nuevo sector, y esta bestia puede salir de su miseria. De lo contrario, voy a perder la cordura.


Parece que su empleador necesita hablar con los clientes y explicarles la situación para averiguar cuál es la mejor manera de ayudarlos, incluso si eso significa que tienen que pagar más al principio, porque a la larga ahorrarán.

7

Trabajé para una pequeña empresa de software que tenía un par de aplicaciones de DOS que se actualizaron para manejar Y2K, reescritas como una aplicación de Windows de 16 bits y luego totalmente reescritas como una aplicación de 32 bits con una función 'pequeña' adicional (en última instancia, solo utilizada por un cliente) que impactó toda la estructura.

Mover el código de 16 bits a 32 podría haber sido hecho en un mes por una persona, pero NOOOOOOOOO, tuvimos que volver a escribirlo para que sea Muuuuuuuuuuuuuuuuuuuuucho mejor. Esto podría adaptarse para otras industrias, tendría especificaciones completas y código psuedo incluso antes de comenzar. Se crearon las especificaciones, pero tomó tanto tiempo que ni siquiera hubo tiempo para escribir el código real. Fue lanzado tarde, con más errores que los 16 bits 'iniciados' (estaba en v.3.0 y finalmente hasta el punto en que casi lo hicimos una semana sin que alguien informara un nuevo error).

Pensarías que reescribir la misma aplicación 3-4 veces traería algunas mejoras, pero no creo que un front-end GUI pueda justificarse tanto.

Este fue mi primer trabajo de TI como jefe de soporte técnico. Debería escribir un libro sobre cómo no desarrollar software para distribución. Obviamente cometimos muchos errores, pero el hecho de que continuamos reescribiendo aplicaciones agravó la incompetencia.


5

Tuve un caso muy similar al tuyo, solo que el código ni siquiera estaba usando Struts. Lo que hice en cambio fue apuntar a áreas que eran particularmente malas y que causaban muchos problemas. Este enfoque dirigido nos hizo progresivamente mejores.

Durante un período de 2 años, trabajamos en la refactorización de piezas y piezas junto con el trabajo de mejora. Siempre trabajamos una tarea de "Endurecimiento" en un plan de proyecto. Al centrarnos en las áreas específicas que no funcionaron bien, obtuvimos el máximo rendimiento. Lo que funcionó lo dejamos solo. También es crítico que este trabajo se haya realizado en el curso del desarrollo normal y se haya publicado. El problema con una gran reescritura es que te vas por un año o más y luego, cuando regresas, todo cambió de todos modos y algunos de los errores desagradables se suavizaron y perdiste tu ROI.

Nunca reescribimos todo el asunto. Sin embargo, dejamos de usar esa plataforma para nuevos trabajos y lanzamos una nueva plataforma nueva para un gran proyecto nuevo. Eso fue aprobado y entregamos un excelente producto en un tiempo razonable.


5

Entonces, aquí estoy sentado en mi escritorio, comencé a reescribir el código para este desastre absoluto de un gran archivo aspx, la base de datos detrás de él, y reemplazar la interfaz de MS Access para el MsSQL.

Este programa asp está plagado de cosas como

  • include(close.aspx) que en su interior tiene una línea de código que cierra la última conexión de base de datos abierta.
  • El código heredado acaba de comentar al azar
  • Sin consideración por la seguridad
  • Código de espagueti, miles de líneas. Todo en un solo archivo.
  • Funciones y variables sin un significado claro detrás de sus nombres.

Si alguna vez necesitamos hacer un ligero cambio, es como jugar cinco juegos simultáneos de kal-toh en modo pesadilla.

Fui contratado para replicar la funcionalidad y hacer un producto que pudiera ser personalizable y vendible para otros en la industria. El problema es que esto se ha escrito en los últimos 10 años para satisfacer todas las necesidades comerciales (bueno, de todos modos diría que cinco o seis sigma de ellas).

Si no quisiéramos vender el producto, probablemente no habrían necesitado una reescritura, ya que hace la mayor parte de lo que quieren, tal vez no con elegancia, pero no fue prudente gastar el dinero en hacer un buen código 'hacer la misma cosa.'


4

Joel Spolsky tiene un excelente artículo sobre esto: Cosas que nunca debes hacer, Parte I

Por el título que puedes decir, es un poco unilateral (habla sobre por qué nunca deberías tirar código) IMO, hay mucha verdad, recientemente vi un video de channel9 en el 25 Aniversario de Excel donde algunos desarrolladores dijeron, cómo incluso hoy, si miraba la fuente, verificaría la revisión y terminaría volviendo al código que Excel utilizó y que fue escrito hace 20 años.

No se puede estar 100% seguro (cuando aún Netscape comete errores (de Joels artículo)), me sentí como el artículo de Joel fue enviado a Dios, porque puedo ser pesimista y encanta tirar pensamiento código Siempre puede escribir mejor un segundo tiempo, pero ahora me he dado cuenta de que esto solo cuesta mucho .

Para dar una respuesta concreta, solo diría que necesita hacer un análisis exhaustivo de Costo vs Valor .

Mi mundo real: una aplicación de Silverlight que estoy desarrollando v0.6 hasta ahora tiene un desorden de llamadas asíncronas que hacen que el código sea tan complicado. Desde que descubrí las extensiones reactivas esta semana, realmente quiero volver a escribir la mayor parte del código, pero ahora ¿qué le digo a mi cliente? El programa funciona perfectamente bien (con algunas pérdidas de memoria) pero ¿no les importa? No puedo decirles Oh, estoy tomando 2-3 semanas más porque quiero volver a hacer algo. Sin embargo, voy a bifurcar el código y volver a escribirlo / jugarlo en mi tiempo libre .

Solo mis 2 centavos, ¿vale?


Su primera implementación es frecuentemente subóptima porque hay cosas que aún no sabe cuando diseña. Por lo general, esto tendrá una forma refactible en lugar de comenzar de nuevo, ya que lo más probable es que hayas hecho algo bien. Si no, es una prueba de concepto, y ELLOS frecuentemente necesitan ser descartados.

+1 para ramificación. Mucha gente dice "no reescribir" porque parece que está tirando código viejo, pero no lo está. Puede tener bases de código paralelas.
lunchmeat317

Para ser justos, si usted es una persona que le pide consejo a Joel Spolsky, entonces no debe hacer una reescritura completa :-) Y, de lo contrario, solo debe reescribir si puede convencer a todas las partes interesadas de por qué los argumentos de Joel no cuentan en tu caso específico
gnasher729

3

La solución existente no escala.

Te estoy mirando, MS Access.


No estoy seguro de si estás mirando la correcta. Jet Red no estaba destinado a escalar nunca, AFAIK.
JensG

¿Cómo responde esto a la pregunta que se hace?
mosquito

3

Según Joel, las grandes reescrituras son el peor error estratégico que una empresa puede cometer:

Cosas que nunca debes hacer, Parte I

... Es importante recordar que cuando comienzas desde cero no hay absolutamente ninguna razón para creer que vas a hacer un mejor trabajo que la primera vez. En primer lugar, probablemente ni siquiera tenga el mismo equipo de programación que trabajó en la versión uno, por lo que en realidad no tiene "más experiencia". Simplemente volverá a cometer la mayoría de los viejos errores e introducirá algunos problemas nuevos que no estaban en la versión original.

El viejo mantra construido para tirar es peligroso cuando se aplica a aplicaciones comerciales a gran escala. Si está escribiendo código experimentalmente, es posible que desee extraer la función que escribió la semana pasada cuando piensa en un algoritmo mejor. Esta bien. Es posible que desee refactorizar una clase para que sea más fácil de usar. Eso también está bien. Pero desechar todo el programa es una locura peligrosa, y si Netscape realmente tuviera supervisión de un adulto con experiencia en la industria del software, es posible que no se hayan disparado tanto en el pie.


55
Sí. Estaba buscando algunos argumentos en contra de eso. Tiene que hacerse a veces.
Jonn

3
Cada argumento de reescritura no está completo sin una referencia al artículo de Joel. Lo que digo es que el artículo tiene algunos puntos buenos, pero maldita sea, piense usted mismo. Quizás deberías decir por qué estás de acuerdo con el artículo. No estoy de acuerdo con Joel personalmente, simplemente porque a veces es mejor cavar un nuevo agujero que aburrirse más en el pozo que nunca termina con el dolor. En cualquier caso, una reescritura, creo, traería a la superficie esos procesos comerciales latentes para su reevaluación.
Ahmad el

El artículo de Joels es bueno, ya que explica cuidadosamente lo mal que pueden ir las cosas .

66
Joel usa un ejemplo del navegador Netscape Navigator. Es un producto de consumo que estaba en medio de una curva de crecimiento masivo que trabajaba contra la competencia con un gran presupuesto. Fue un error estratégico para Netscape. Por otro lado, los proyectos internos personalizados de software "empresarial" son diferentes. No te estás apresurando por la cuota de mercado. No hay peligro de que todos sus usuarios se vayan a un producto competitivo. Todo se reduce a una decisión comercial: ¿el costo de una reescritura se pagará a largo plazo y / o es la mejor manera de alcanzar los objetivos comerciales?
Scott Whitlock

Pensé que Joel tocó el concepto clave, "Nunca se sabe qué decisiones no se han documentado que tendrá que aprender de la manera difícil"
MathAttack

2

Nunca, siempre refactorice, la verdad es que si el código fue escrito por usted, no podrá hacerlo mejor.

A menos que desee cambiar la tecnología, el código carece de cualquier estructura (lo vi hace mucho tiempo en algún sitio web de PHP, el autor simplemente copió / pegó spahgetti en lugar de incluir / clase / función) o se hizo cargo de algo de otra persona que es Muy mal escrito.

Todo debe estar diseñado como una caja negra. Modular, API simple, lo que hay dentro ... eso es menos importante :) Si tienes espagueti, tal vez puedas cerrarlo dentro de una caja negra, para que no contamine un buen código.



+1 Me gusta tu opinión, ¿me gustaría saber qué piensas sobre volver a escribir en un nuevo conjunto de API? (Vea mi respuesta a continuación)
gideon

Sí, estos son buenos puntos. Microsoft reescribió el código winXP y ni siquiera pudieron eliminar / copiar archivos correctamente en la primera versión comercial de Vista. Mientras que cuando solo estaban progresando en su base de código, recibíamos una mejor calidad constantemente (W3.11 => W95 => W98 => ME => XP), Vista en la que reescribieron muchas partes centrales fue un desastre. Para la nueva API ... separaría el código actual para tener todo lo que pudiera intacto, y usaría la nueva API en la capa superior. P.ej. sus clases principales permanecen como están, pero la integración se realiza utilizando una nueva API A menos que todo esté tan desordenado que no se pueda hacer nada más que comenzar desde 0.
Slawek

1
"... la verdad es que si el código fue escrito por usted, no podrá hacerlo mejor". Votaría esta publicación si tuviera suficiente representante. Eso es irrevocable, poco realista e implica que de alguna manera las personas no pueden avanzar al punto donde el código que escriben es una mejora en el código que escribieron en el pasado.
JᴀʏMᴇᴇ

1
@ JᴀʏMᴇᴇ: De acuerdo: si no aprendió nada y no ganó experiencia al implementarlo por primera vez y / o si no puede hacer un mejor trabajo ahora que tiene más conocimiento / experiencia / retrospectiva; entonces eres una papa y no un programador.
Brendan

2

Creo que la razón principal que justifica las reescrituras son los cambios de plataforma. Por ejemplo, si tiene una aplicación GUI de escritorio de Windows y el propietario de la compañía decide que quiere que la próxima versión sea una aplicación basada en la web. Aunque no siempre, en mi experiencia, la mayoría de las veces esto requerirá una reescritura, a menos que los desarrolladores originales escribieran código muy modular y reutilizable (casi no sucede).


2

Hay una broma clásica sobre "si fuera a XI no comenzaría desde aquí".

Una vez tomé un trabajo, donde la principal motivación del empleador era traer talento a bordo para lanzar una reescritura de una aplicación crítica para el negocio. La pregunta que no hice fue, ¿por qué has terminado en esta situación en primer lugar? Debería haber sido una bandera roja, si la causa fue

  • demasiada presión para agregar funcionalidad para mantener y refactorizar gradualmente a la bestia,

  • muy poca habilidad en el departamento,

  • muy poca aceptación por parte de los clientes o la gerencia para un trabajo incremental,

o lo que sea, y esto causa una supuración hasta que el problema alcanza un nivel intolerable.

Así que voy a estar de acuerdo con Joel en que una reescritura masiva es probablemente una muy mala idea, a menos que tenga pruebas firmes de que todas las razones subyacentes detrás de por qué una reescritura importante parece tan necesaria se han abordado , es muy probable voy a repetir el problema a su debido tiempo.


2

Es la respuesta cuando la reescritura permite a la organización seguir una estrategia que ofrece una ventaja competitiva o le permite servir mejor a sus clientes de una manera que la arquitectura actual no puede acomodar.

En cambio, las reescrituras suelen aliviar las preocupaciones de la administración:

  • todo debería estar en .NET,
  • nuestros desarrolladores dicen que nuestro código apesta,
  • nos estamos quedando atrás técnicamente
  • no podremos encontrar recursos para apoyar nuestro sistema o, muy a menudo,
  • Han pasado diez años, así que es hora.

La mayoría de estas preocupaciones no se materializarán y, si lo hicieran, podrían manejarse. Ese último, sin embargo, es el peor. Es esencialmente: no tenemos una razón.

Sí, como el automóvil, un sistema comenzará a mostrar signos de desgaste. Esto puede aliviarse mediante el mantenimiento de rutina. Ya sabes lo que dicen sobre cómo un poco de mantenimiento preventivo es muy útil. Como inversión, el servicio de rutina (es decir, refactorización y estandarización) casi siempre conllevará un costo menor que una reescritura.

Sin embargo, tenemos que anticipar que eventualmente será necesaria una reescritura. Establezca fechas y planifique tentativamente para ello, pero a medida que la fecha se acerque más tarde a favor de alcanzar otros objetivos estratégicos hasta que tenga una razón convincente como ...

En los últimos años hemos perdido la oportunidad de ganar grandes cuentas porque no pudimos satisfacer sus necesidades de manera oportuna o costosa. Nuestro sistema se reescribirá utilizando una arquitectura extensible que permite que las personalizaciones se conecten (y no se codifiquen como lo hacemos actualmente). Esto disminuirá drásticamente el tiempo y el costo de acomodar a los clientes con necesidades especiales. Ganaremos más cuentas y satisfaceremos mejor las necesidades de nuestros clientes actuales.


Parece que deberías poder refactorizar en esa dirección. Al refactorizar, tiene en mente crear la flexibilidad que necesita para que la próxima característica sea fácil de escribir.
Ian

1

Cuando se pasa a una tecnología completamente nueva, se necesita para proporcionar la funcionalidad deseada.

"Completamente nuevo": si planea reescribir pero usar la misma plataforma, entonces la refactorización y la reestructuración juiciosa es casi seguro la mejor solución. "Plataforma", como se usa aquí, es algo vago: considérelo que incluye lenguaje, y tal vez el SO (que se extiende desde Linux a Windows o viceversa me viene a la mente), pero probablemente no el marco (por ejemplo, reemplazar Struts con Spring).

"Necesario para proporcionar la funcionalidad deseada": alrededor del año 2000, inicié un proyecto para reescribir un componente principal del servidor en Java desde C ++ para permitir el enhebrado inmediato, un caché de objetos y transacciones controladas por el cliente. En ese momento, había varias bibliotecas de subprocesos para C ++ que habríamos tenido que admitir, y la habilitación de subprocesos de gran parte de nuestro código de base de datos habría requerido una reescritura casi total. En cuanto a las transacciones controladas por el cliente ... no sucederá con la arquitectura antigua.

Incluso entonces, no estoy seguro de haber hecho la reescritura, excepto que tenía un conocimiento detallado del comportamiento del sistema actual, y era un código relativamente limpio que no había desarrollado muchas verrugas en sus 11 años de vida.


0

Para decidir el punto en el que debe comenzar de nuevo, debe determinar dónde el valor de continuar en su proyecto actual es inferior al valor de comenzar de nuevo.

La razón por la que esto es tan difícil es que hace una estimación precisa de la velocidad de desarrollo del equipo en el nuevo proyecto hasta que realmente lo inicie. Dicho esto, si, utilizando una estimación MUY conservadora de su velocidad de desarrollo en el nuevo proyecto, se estima que el proyecto ofrece un retorno de la inversión que vale la pena, entonces tiene un argumento comercial para comenzar de nuevo.


0

Con mi métrica, debe cumplir dos condiciones para justificar una reescritura:

  1. Después de la reescritura, las nuevas características serán más fáciles / rápidas / menos complicadas de escribir
  2. La reescritura llevará menos o igual tiempo que agregar la nueva característica actual.

Incluso entonces desea limitar el alcance de su reescritura. Por ejemplo, teníamos un software heredado en una compañía que estaba escrito en C ++ con la mentalidad de un programador de C que no sabía sobre modularidad. El resultado final fue un código de speghetti en forma de una máquina de estados finitos que abarcaba múltiples clases y DLL. Teníamos un nuevo requisito que era muy diferente de los supuestos incorporados en el código, y que iba a ser parte del valor agregado patentado de la compañía para sus clientes.

Después de pasar tiempo con el código implementando otras características, tuve una muy buena idea de cuánto tiempo tomaría averiguar cuál de las varias declaraciones de cambio necesitaría cambiar, etc. Mi propuesta fue reescribir la sección de código que estaba la enorme máquina de estados finitos: debería llevarme menos tiempo que extender el código existente. Pude consolidar en una DLL lo que solía ser tres, y proporcionar una estructura mucho más orientada a objetos que haría mucho más fácil hacer la nueva funcionalidad crítica, además de facilitar la adición de nueva funcionalidad.

Había otras tres aplicaciones que usaban las bibliotecas que necesitaban reescribir, así que no quería tener que reescribir completamente esos programas. El alcance se limitaba a la biblioteca y lo que era necesario para vincular la biblioteca al software existente. Mis estimaciones no estaban lejos, y el trabajo se amortizó. Poco después del rediseño, me encargaron agregar una nueva característica. Lo que me hubiera llevado una semana con la estructura anterior solo me llevó un día con la estructura nueva.


0

El OP no indica el alcance del proyecto, pero la pista principal que tomé fue "escrito en [idioma / dialecto] antiguo usando viejos [libs]", que para mí son los principales impulsores de una reescritura. De hecho, la mayoría de los proyectos en los que he estado involucrado en cualquier longevidad significativa del mercado finalmente se dan cuenta de que las actualizaciones de compilación / interpretación / sistemas operativos son una tarea importante para mantener la base del código.

En resumen, planearía la reescritura en fases, priorizando primero la actualización en libs. Puede ser que, en el camino, pueda colarse en otras optimizaciones, pero el hecho de que planea diferir algunos cambios a una fase posterior puede ser una excelente manera de cumplir con el cronograma y evitar "atascarse".


0

Bueno, puedo imaginar escenarios hipotéticos en los que podría tirar la base de código existente (situaciones hipotéticas en las que las bolas de bucky tienen cero peso y volumen, donde a nadie le importa si perdemos semanas o meses de productividad mientras luchamos para agregar características y errores pasados ​​por alto) correcciones en el sistema, y ​​donde el sistema es tan trivial y odiado por una organización que puedo reemplazar todo el ejército de servidores con notepad ++ y una netbook en diez minutos y aumentar la productividad y la moral de todos en todos los ámbitos).

Para casi cualquier escenario del mundo real, aunque solo recomendaría refactorizar en su lugar. ^ _ ^. Tiende a haber demasiados costos ocultos e imprevistos para reescribir cuando los requisitos legales y comerciales indocumentados comienzan a aparecer y el último minuto pirateando cosas juntos en el último minuto comienza a producirse.

== Refactorización en el lugar para un bien mayor, y cosas ==

Refactorizando el código heredado con el viejo enfoque incremental regular,

  1. Si tiene la oportunidad, conviértase a UML y documente la pequeña arquitectura knarly que existe.
  2. Si está en control de versiones, busque generar algunos informes de cambio de código y averiguar qué archivos y secciones de archivos se han cambiado con más frecuencia. Tome nota de estos, ya que probablemente serán las áreas con las que querrá tratar primero.
  3. Antes de cambiar cualquier código, siempre intente agregar algo de cobertura de prueba (incluso las pruebas funcionales de pila completa fea lo harán). Si se trata de una lógica de extracción de código procesal desordenada grande, tiene la intención de cambiar a algún método o función razonablemente nombrado y, si es posible, agregue algunos casos de prueba que verifiquen su nuevo método. haga cualquier truco horrible que tenga que hacer y, si es posible, paramatice el cambio si es algo comúnmente modificado como el ancho del margen o el título, por lo que será un poco más sencillo actualizar la próxima vez.
  4. Use el patrón del adaptador y evite ocultar los fragmentos de código heredado bajo una alfombra de clases y métodos con nombres lógicos para que, para las tareas más comunes que usted y los otros desarrolladores realicen, no tenga que preocuparse por los fragmentos de miedo que están sucediendo detrás de espaldas a esos pequeños métodos y clases limpios y escondidos detrás de ese código heredado, como esas agradables familias que mantienen a los zombis asesinos deformados y ex miembros de la familia en los graneros para que no estropeen el día a día de las granja . . normalmente.
  5. A medida que continúe reduciendo y limpiando las secciones del código, continúe aumentando su cobertura de prueba. Ahora puede profundizar aún más y "reescribir" aún más código cuando sea necesario / deseado con una confianza cada vez mayor.
  6. Repita o aplique enfoques de refactorización adicionales para continuar mejorando su base de código.

Ramificación por abstracción

  1. Defina el punto problemático en el código que desea eliminar (la capa de persistencia, el generador de pdf, el mecanismo de conteo de facturas, el generador de widgets, etc.).
  2. ejecute (escriba si es necesario) algunos casos de prueba funcionales (automatizados o manuales, pero usted sabe que son automáticos) contra la base del código que apunta a esta funcionalidad junto con el comportamiento general.
  3. Extraiga la lógica relacionada con ese componente de la base de origen existente en una clase con alguna interfaz razonable.
  4. Verifique que todo el código esté usando la nueva interfaz para realizar la actividad X, que anteriormente estaba dispersa al azar en todo el código (agregue la base del código, agregue un rastro a la nueva clase y verifique que las páginas que deberían llamarlo son, etc.), y que puede controlar qué implementación se utilizará modificando un solo archivo. (registro de objetos, clase de fábrica, lo que sea IActivityXClass = Settings.AcitivtyXImplementer ();)
  5. vuelva a ejecutar casos de prueba funcionales que verifiquen que todo sigue funcionando con toda la actividad X lanzada en su nueva clase.
  6. Escriba pruebas unitarias cuando sea posible alrededor de la nueva clase de envoltura X de actividad.
  7. implementar una nueva clase con una lógica de espagueti menos loca que la implementación heredada que se adhiere a la misma interfaz que la clase heredada.
  8. verifique que la nueva clase pase las pruebas unitarias que escribió para la clase heredada.
  9. actualice su código cambiando el registro / método de fábrica / lo que sea para usar la nueva clase en lugar de la clase anterior.
  10. verifique que sus pruebas funcionales aún pasen.

Principio abierto cerrado y una lógica comercial común / capa de persistencia

Hasta cierto punto, es posible que pueda salirse con la separación de su presentación, negocio y capa de persistencia y escribir una nueva aplicación que sea totalmente compatible con la solución heredada, o como mínimo, aún puede manejar los datos ingresados ​​por la solución heredada. Probablemente no recomendaría este enfoque, pero a veces es un compromiso razonable de tiempo / horario / recursos / nueva funcionalidad requerida.

  • Como mínimo, separe la capa de presentación de las capas comerciales y de persistencia.
  • Implemente una nueva interfaz de usuario y una mejor capa de presentación que utilice la misma capa común de negocios y persistencia.
  • Verifique que los datos creados con la nueva interfaz de usuario no rompan la interfaz de usuario anterior. (vas a estar en agua caliente si los usuarios de la nueva herramienta interrumpen a los usuarios de la herramienta anterior). Si se esfuerza por lograr una compatibilidad total con versiones anteriores, debe guardar todo en las mismas capas de persistencia. Si solo desea compatibilidad con la nueva herramienta, utilice una nueva base de datos y nuevas tablas o tablas de extensión para rastrear datos que no están en el sistema heredado.
  • Para la nueva funcionalidad y la capa de persistencia necesita agregar nuevas tablas y métodos, no cambie la lógica heredada existente, ni agregue columnas, restricciones a las tablas existentes. por ejemplo, si necesita comenzar a rastrear el contacto de emergencia de los empleadores y algunos otros campos no modifican la tabla de empleados existente (no tenemos idea de qué suposiciones hacen los datos heredados sobre esa tabla) agregue una tabla de extensión employee_ext id, employee_id, emergency_contact_id, etc_id.
  • Migre lentamente a los usuarios al nuevo sistema. si es posible, coloque el sistema heredado en modo de solo lectura, o simplemente agregue algunas advertencias para informar a los usuarios que ya no estará disponible después de la fecha X.
  • Implemente cualquier funcionalidad de prioridad alta o requisitos comerciales en la nueva interfaz de usuario
  • pasar sobre la base de usuarios.
  • continúe limpiando la lógica de negocios y los usuarios de la capa de persistencia otras metodologías de refactorización.

0

Diría que hay una tercera categoría en el espacio Refactor vs Rewrite ... Y eso es actualizar sus versiones de lenguaje, compiladores y bibliotecas ... A veces, simplemente adoptar técnicas de codificación modernas tiene un gran beneficio.

Tomemos C # por ejemplo, el código v7 escribe mucho más limpio, más seguro y más conciso que v2. Cosas como Elvis y el operador de fusión nula ayudan mucho.

Cambiar los compiladores también podría dar nueva vida a un código más antiguo. Entonces, podrían crearse nuevas bibliotecas que faciliten el trabajo y la implementación ... La creación de redes es un gran ejemplo de un espectro de dificultades de implementación.

También: sistemas Linux integrados ... Piense en instalar nuevas herramientas: cambie a git desde svn. o agregue Vi a su sistema, etc., etc.

No siempre tiene que ser un refactor vs reescribir ... Su arquitectura probablemente necesite mejoras, pero funciona ... tal vez solo necesite pensar en cómo se escribe su código, etc.


-2

No creo haber visto personas reescribir una aplicación heredada.

Lo que sucede es que las personas escriben aplicaciones totalmente nuevas con una arquitectura, tecnologías, etc. diferentes que tienen algunas características en común con la generación anterior. Y se llama aplicación 2.0, pero de hecho tiene muy pocas cosas en común con el original.

Pero esto no es realmente una "reescritura" del original en el sentido de que está tratando de lograr la paridad de características.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.