¿Es útil el mini-refactorizar código con la esperanza de mejorar la calidad, o es simplemente "mover el código" sin mucho beneficio?

12

Ejemplo

Encontré un código monolítico que hace "todo" en un solo lugar: cargando datos de la base de datos, mostrando el marcado HTML, actuando como un enrutador / controlador / acción. Comencé a aplicar el código de base de datos móvil de SRP en su propio archivo, proporcionando un mejor nombre para las cosas, y todo se veía bien, pero luego comencé a tener dudas sobre por qué estoy haciendo esto.

¿Por qué refactorizar? ¿Cuál es el propósito? ¿Es inútil? ¿Cuál es el beneficio? Tenga en cuenta que en su mayoría dejé el archivo monolítico tal como está, pero refactoricé solo la porción más pequeña que era relevante para el área donde necesitaba hacer algo de trabajo.

Código original:

Para dar un ejemplo concreto, me encontré con este fragmento de código: carga las especificaciones del producto ya sea por una identificación de producto conocida o por una identificación de versión seleccionada por el usuario:

if ($verid)
    $sql1 = "SELECT * FROM product_spec WHERE id = " . clean_input($verid);
else
    $sql1 = "SELECT * FROM product_spec WHERE product_id = " . clean_input($productid) ;
$result1 = query($sql1);
$row1 = fetch_array($result1);
/* html markup follows */

Refactorización:

Como estoy haciendo un trabajo que requiere que cambie las cosas en esta parte específica del código, lo cambié para usar el patrón de repositorio y lo actualicé para usar las instalaciones MySQL orientadas a objetos:

//some implementation details omitted 
$this->repository = new SpecRepository($mysql);
if ($verid)
    $row1 = $this->repository->getSpecByVersion($verid);
else 
    $row1 = $this->repository->getSpecByProductId($productid);
/* html markup follows to be refactored or left alone till another time*/

//added new class:
class SpecRepository extends MySqlRepository
{

    function getSpecByVersion(int $verid)
    {
        return $this->getMySql()->paramQuery("
            SELECT * FROM product_spec WHERE id = ?
        ", $verid)->getSingleArray();
    }

    function getSpecByProductId(int $productid)
    {
        return $this->getMySql()->paramQuery("
            SELECT * FROM product_spec WHERE product_id = ?
        ", $productid)->getSingleArray();
    }
}

¿Debo hacer esto?

Mirando hacia atrás a los cambios, el código sigue ahí, código con la misma funcionalidad, pero en diferentes archivos, diferentes nombres, lugares, usando un estilo más orientado a objetos en lugar de procedimientos. En realidad, es divertido notar que el código refactorizado se ve mucho más hinchado a pesar de tener la misma funcionalidad.

Preveo algunas respuestas que dicen "si no sabes las razones por las que refactorizas, no lo hagas", y tal vez podría estar de acuerdo. Mis razones son mejorar la calidad del código con el tiempo (y espero que lo haga siguiendo el SRP y otros principios).

¿Son esas buenas razones o estoy perdiendo el tiempo en "reorganizar el código" de esta manera? La refactorización general se siente un poco como pisar agua para ser honesto: lleva tiempo y se vuelve más "separado" en lo que respecta a SRP, pero a pesar de mis buenas intenciones, no siento que esté haciendo mejoras increíbles. Por lo tanto, debatir si es mejor dejar el código como antes y no refactorizar.

¿Por qué refactoré en primer lugar?

En mi caso, estoy agregando nuevas funcionalidades para una nueva línea de productos, por lo que tengo que seguir la estructura de código existente para líneas de productos similares o escribir la mía.

Dennis
fuente
OT, pero algo a considerar es que Select * from ..puede considerarse un antipatrón. Ver stackoverflow.com/q/3639861/31326
Peter M
1
@PeterM: a menos que esté escribiendo un ORM, en cuyo caso se select *convierte en una "mejor práctica".
Robert Harvey
@RobertHarvey En cuyo caso, me pregunto por qué está escribiendo su propio ORM: D
Peter M
He hecho refactores por causas más irrelevantes. Reducir la deuda técnica es bueno tan pronto como los beneficios superen los costos. Parece que este es tu caso. La pregunta es: ¿Tiene las pruebas correctas para asegurarse de que el código todavía funciona como se esperaba?
Laiv
1
La confiabilidad debe ser la métrica predominante cuando se refactoriza la OMI, no la mantenibilidad que se relaciona con la capacidad de cambio, ya que la confiabilidad reduce las razones para que las cosas cambien en primer lugar. Mejora la estabilidad, y el código que es perfectamente estable y confiable y cumple con todo lo que necesita hacer tiene un bajo costo de mantenimiento, ya que tiene muy pocas razones para cambiar. Trate de reducir las razones por las cuales las cosas cambian en lugar de tratar de hacer que sean lo más fáciles de cambiar. Buscar lo primero también tenderá a dar algo de lo último.

Respuestas:

13

En el ejemplo concreto que dio, la refactorización podría no haber sido necesaria, al menos no todavía. Pero viste que había un potencial de código más desordenado en el futuro que habría llevado a una refactorización más larga y tediosa, así que tomaste la iniciativa y limpiaste las cosas ahora.

También diría que la ventaja que obtuvo aquí fue el código que es más fácil de entender, al menos desde mi punto de vista, como alguien que no conoce su base de código.


En general:

¿La refactorización facilita que otros desarrolladores entiendan su código?

¿La refactorización facilita la mejora del código?

¿La refactorización facilita el mantenimiento del código?

¿La refactorización facilita la depuración?

Si la respuesta a cualquiera de estas preguntas fue "sí", entonces valió la pena, ¿y fue más que simplemente "mover el código?

Si la respuesta a todas estas preguntas fue "no", entonces tal vez no fue necesario refactorizarlas.

El tamaño final de la base del código podría ser mayor en términos de LoC (aunque algunas refactorizaciones pueden reducir la base del código al simplificar el código redundante), pero eso no debería ser un factor si hay otras ganancias.

FrustratedWithFormsDesigner
fuente
9

La refactorización se hincha si está desmontando un monolito.

Sin embargo, ya encontraste el beneficio.

"En mi caso, estoy agregando nuevas funcionalidades para una nueva línea de productos".

No se puede agregar funcionalidad a un monolito muy fácilmente sin romper cosas.

Dijiste que el mismo código está obteniendo los datos y haciendo el marcado. Genial, hasta que desee cambiar las columnas que está seleccionando y manipulando para mostrar en ciertas condiciones. De repente, su código de marcado deja de funcionar en un caso determinado y tiene que refactorizar.

Diácono
fuente
sí, pero también puedo seguir agregando al monolito incluso para una nueva funcionalidad :) es decir, agregaría un nuevo if/elsebloque y seguiría el estilo original del código para agregar declaraciones SQL, y luego pondría un nuevo marcado HTML en el mismo archivo monolítico. Y para cambiar las columnas, encontraría el bloque if / else responsable de alojar las líneas SQL, y editaría ese SQL para realizar cambios en las columnas, sin dividir ese archivo.
Dennis
Con el enfoque refactorizado, probablemente primero iría al bloque if / else en el monolito para ver que necesito ir al archivo de repositorio separado para cambiar SQL. O si trabajé en ese monolito recientemente, sabría que debo ir directamente al archivo del repositorio, evitando el monolito. O si busqué en mi base de código el SQL específico, la búsqueda me colocaría en el nuevo archivo del repositorio, sin pasar por alto el monolito
Dennis
2

Considero refactorizar si sé que tendré que trabajar meses o incluso años con ese proyecto. Si tengo que hacer un cambio aquí y allá, entonces me resisto a la necesidad de mover el código.

La forma preferida de refactorizar es cuando tengo un código base con algún tipo de pruebas automatizadas. Aun así, tengo que tener mucho cuidado con lo que cambio para asegurarme de que las cosas funcionen como antes. Perderá la confianza muy pronto si entrega errores en otras áreas que no sea la que supuestamente debería funcionar.

Valoro la refactorización por las siguientes razones:

  • Entiendo mejor el código
  • Elimino el código duplicado, así que si tengo un error en un lugar, no tengo que buscar en todos los lugares donde se copió el código
  • Creo clases que tienen su rol bien definido. Por ejemplo, puedo reutilizar el mismo repositorio para hidratar una página HTML, un feed XML o un trabajo en segundo plano. Esto no hubiera sido posible si el código de acceso a la base de datos solo se encuentra en la clase HTML
  • Renombro variables, métodos, clases, módulos, carpetas si no coinciden con la realidad actual; Yo commentel código a través de nombres de variables, nombres de métodos
  • Resuelvo errores complejos con una combinación entre pruebas unitarias y refactorización
  • Tengo la flexibilidad de reemplazar módulos completos, paquetes. Si el ORM actual está desactualizado, tiene errores o no se mantiene, entonces es más fácil reemplazarlo si no está extendido por todo el proyecto.
  • Descubro nuevas capacidades o mejoras que resultan en propuestas para el cliente.
  • Creé componentes reutilizables. Este fue uno de los mayores beneficios porque el trabajo realizado en ese proyecto podría haberse reutilizado para otro cliente / proyecto.
  • A menudo empiezo con la solución más tonta, codificada y loca y la refactorizo ​​más tarde una vez que obtengo más información. Este último momento podría ser hoy o tal vez después de varios días. No quiero dedicar demasiado tiempo a architectla solución. Esto vendrá mientras escribes, reescribiendo el código de un lado a otro.

En un mundo perfecto, las refactorizaciones deben verificarse mediante pruebas unitarias, pruebas de integración, etc. Si no hay ninguno, intente agregarlos. También algunos IDE pueden ayudar mucho. Usualmente uso un complemento que cuesta dinero. Quiero pasar poco tiempo con refactorizaciones y ser muy eficiente al respecto.

También introduje errores. Puedo ver por qué algunos QA preguntan. are you guys doing refactoring? because everything stopped working!Este es el riesgo que el equipo debe asumir y siempre tenemos que ser transparentes al respecto.

Durante los años descubrí que la refactorización continua mejoraba mi productividad. Fue mucho más fácil agregar nuevas funciones. Además, las grandes reconstrucciones no requirieron una reescritura completa, como sucedió a menudo cuando la base del código no se adaptó a la evolución del producto. También es divertido.

Adrian Iftode
fuente
Esto está muy cerca de un voto negativo: no hay un "yo" en "Programador de computadoras". Por 'yo', ¿me 'yo' (voto negativo) o te refieres a los desarrolladores que trabajan en el código ahora y en el futuro?
mattnz
Yo también lo noté, pero lo puse en palabras. También trabajé como desarrollador en solitario durante muchos años y cuando escribí esta respuesta reiteré principalmente de esa experiencia. Aunque podría reemplazar Icon to.
Adrian Iftode
1

Creo que la pregunta que tienes que hacerte no es tanto "¿Hay algún beneficio?" pero "¿Quién pagará por este trabajo?"

Todos podemos discutir sobre los beneficios percibidos hasta el final de los días, pero a menos que tenga un cliente que crea en esos beneficios y los valore lo suficiente como para pagar los cambios, no debería estar haciéndolos.

Es muy fácil cuando estás en un equipo de desarrollo en algún lugar para ver el código y quieres refactorizarlo para que sea 'mejor'. Pero poner un valor en 'mejor código' cuando el producto ya funciona es realmente difícil.

Cosas como el "desarrollo más rápido de nuevas características" no son suficientes porque son solo costos reducidos. Necesitas generar ventas.

Ewan
fuente
1

En mi opinión, refactorizar es una buena inversión.

De lo contrario, pronto obtendrá deudas técnicas (problemas no resueltos, código incorrecto que solo funciona bajo ciertas condiciones, etc.). Las deudas técnicas pronto se harán cada vez más grandes, hasta que el software ya no sea mantenible.

Pero para que la refactorización funcione, también debe invertir en pruebas. Debería crear pruebas automatizadas, idealmente debería tener pruebas unitarias y pruebas de integración. Esto evita que rompas el código existente.

Si tiene que convencer a su jefe o colegas, debe leer algunos libros sobre métodos ágiles (por ejemplo, SCRUM) y el desarrollo basado en pruebas.

bernie
fuente