Est-il utile de mini-refactoriser du code dans l'espoir d'améliorer la qualité, ou est-ce simplement de «déplacer du code» sans grand bénéfice?


12

Exemple

Je suis tombé sur un code monolithique qui fait "tout" en un seul endroit - charger des données à partir de la base de données, montrer le balisage HTML, agir comme un routeur / contrôleur / action. J'ai commencé à appliquer le code de base de données mobile SRP dans son propre fichier, fournissant une meilleure dénomination des choses, et tout semblait bien, mais j'ai ensuite commencé à douter de la raison pour laquelle je fais cela.

Pourquoi refactoriser? Quel est le but? Est-ce inutile? Quel est l'avantage? Notez que j'ai principalement laissé le fichier monolithique tel quel, mais que je n'ai refactorisé que la plus petite partie qui était pertinente pour la zone où j'avais besoin de faire un peu de travail.

Code d'origine:

Pour donner un exemple concret, je suis tombé sur cet extrait de code - il charge les spécifications du produit soit par un identifiant de produit connu, soit par un identifiant de version sélectionné par l'utilisateur:

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 */

Refactoring:

Étant donné que je fais un travail me demandant de changer les choses dans cette partie spécifique du code, je l'ai changé pour utiliser le modèle de référentiel et l'ai mis à niveau pour utiliser les fonctionnalités MySQL orientées objet:

//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();
    }
}

Dois-je faire ça?

En regardant les changements, le code est toujours là, code avec la même fonctionnalité, mais dans des fichiers différents, des noms et des endroits différents, en utilisant un style plus orienté objet plutôt que procédural. En fait, il est amusant de noter que le code refactorisé semble beaucoup plus gonflé malgré les mêmes fonctionnalités.

Je prévois quelques réponses disant "si vous ne connaissez pas les raisons pour lesquelles vous refactorisez, ne le faites pas", et peut-être que je pourrais être d'accord. Mes raisons sont d'améliorer la qualité du code au fil du temps (et j'espère que je le ferai en suivant SRP et d'autres principes).

Ces raisons sont-elles suffisantes ou suis-je en train de perdre mon temps à "réorganiser le code" de cette façon? Pour être honnête, le refactoring global ressemble un peu à de l'eau - cela prend du temps et il devient plus "séparé" en ce qui concerne SRP, mais malgré mes bonnes intentions, je n'ai pas l'impression de faire des améliorations incroyables. Par conséquent, débattre s'il est préférable de laisser le code comme avant et non de refactoriser.

Pourquoi ai-je refactorisé en premier lieu?

Dans mon cas, j'ajoute de nouvelles fonctionnalités pour une nouvelle gamme de produits, je dois donc soit suivre la structure de code existante pour des gammes de produits similaires, soit écrire la mienne.


OT mais quelque chose à considérer est que cela Select * from ..peut être considéré comme un anti-modèle. Voir stackoverflow.com/q/3639861/31326
Peter M

1
@PeterM: à moins que vous n'écriviez un ORM, auquel cas cela select *devient une "meilleure pratique".
Robert Harvey

@RobertHarvey Dans ce cas, je me demande pourquoi vous écrivez votre propre ORM: D
Peter M

J'ai fait des refactors pour des causes plus hors de propos. La réduction de la dette technique est bonne dès que les bénéfices dépassent les coûts. On dirait que c'est votre cas. La question est: avez-vous les bons tests pour vous assurer que le code fonctionne toujours comme prévu?
Laiv

1
La fiabilité devrait être la métrique prédominante lors de la refactorisation de l'OMI, et non la maintenabilité qui se rapporte à la variabilité, car la fiabilité réduit les raisons pour lesquelles les choses changent en premier lieu. Il améliore la stabilité, et le code qui est parfaitement stable et fiable et remplit tout ce qu'il doit faire entraîne peu de coûts de maintenance, car il a très peu de raisons de devoir changer. Cherchez à réduire les raisons pour lesquelles les choses changent au lieu d'essayer de les rendre aussi faciles que possible à changer. La recherche de la première aura également tendance à donner une partie de la seconde.

Réponses:


13

Dans l'exemple concret que vous avez donné, la refactorisation n'était peut-être pas nécessaire, du moins pas encore. Mais vous avez vu qu'il y avait un potentiel de code plus désordonné à l'avenir qui aurait conduit à une refactorisation plus longue et plus fastidieuse, alors vous avez pris l'initiative et nettoyé les choses maintenant.

Je dirais également que l'avantage que vous avez obtenu ici était un code plus facile à comprendre, du moins de mon point de vue, en tant que personne qui ne connaît pas votre base de code.


En général:

Le refactoring permet-il aux autres développeurs de comprendre plus facilement votre code?

Le refactoring facilite-t-il l'amélioration du code?

Le refactoring facilite-t-il la maintenance du code?

Le refactoring facilite-t-il le débogage?

Si la réponse à l'un d'entre eux était "oui", alors cela en valait la peine, et c'était plus que "déplacer du code?"

Si la réponse à toutes ces questions était «non», il n'était peut-être pas nécessaire de la refactoriser.

La taille finale de la base de code peut être plus grande en termes de LoC (bien que certains refactorings puissent réduire la base de code en rationalisant le code redondant), mais cela ne devrait pas être un facteur s'il y a d'autres gains.


9

La refactorisation gonfle si vous démontez un monolithe.

Cependant, vous avez déjà trouvé l'avantage.

"Dans mon cas, j'ajoute de nouvelles fonctionnalités pour une nouvelle gamme de produits."

Vous ne pouvez pas ajouter des fonctionnalités à un monolithe très facilement sans casser des choses.

Vous avez dit que le même code récupère les données et effectue le balisage. Parfait, jusqu'à ce que vous souhaitiez modifier les colonnes que vous sélectionnez et manipulez pour les afficher dans certaines conditions. Soudain, votre code de balisage cesse de fonctionner dans un certain cas et vous devez refactoriser.


oui, mais je peux également continuer à ajouter au monolith même pour de nouvelles fonctionnalités :) c'est-à-dire que j'ajouterais un nouveau if/elsebloc et suivrais le style de code d'origine pour ajouter des instructions SQL, puis je mettrais un nouveau balisage HTML dans le même fichier monolith. Et pour changer les colonnes, je trouverais le bloc if / else responsable du logement des lignes SQL, et éditerais ce SQL pour apporter des modifications aux colonnes, sans casser ce fichier.
Dennis

Avec une approche refactorisée, j'irais probablement d'abord dans le bloc if / else du monolith pour voir que je dois aller dans le fichier de référentiel séparé pour changer SQL. Ou si j'ai travaillé sur ce monolithe récemment, je saurais plutôt aller directement dans le fichier de référentiel, en contournant le monolithe. Ou si je cherchais dans ma base de code le SQL spécifique, la recherche me mettrait dans le nouveau fichier de référentiel en contournant également le monolithe
Dennis

2

J'envisage de refactoring si je sais que je devrai travailler des mois voire des années avec ce projet. Si je dois faire un changement ici et là, alors je résiste à l'envie de déplacer le code.

La façon préférée de refactoriser est quand j'ai un code de base avec une sorte de tests automatisés. Néanmoins, je dois être très prudent avec ce que je change pour m'assurer que les choses fonctionnent comme avant. Vous perdrez confiance très bientôt si vous livrez des bogues dans d'autres domaines que celui que vous êtes censé travailler.

J'apprécie le refactoring pour les raisons suivantes:

  • Je comprends mieux le code
  • Je supprime le code dupliqué, donc si j'ai un bug à un endroit, je n'ai pas besoin de chasser partout où le code a été copié
  • Je crée des classes qui ont leur rôle bien défini. Par exemple, je peux réutiliser le même référentiel pour hydrater une page HTML, un flux XML ou un travail d'arrière-plan. Cela n'aurait pas été possible si le code d'accès à la base de données ne vivait que dans la classe HTML
  • Je renomme les variables, méthodes, classes, modules, dossiers s'ils ne correspondent pas à la réalité actuelle; I commentle code via les noms de variables, les noms de méthodes
  • Je résous des bugs complexes avec une combinaison entre tests unitaires et refactoring
  • J'ai la possibilité de remplacer des modules entiers, des packages. Si l'ORM actuel est obsolète, bogué ou non maintenu, il est plus facile de le remplacer s'il n'est pas réparti sur tout le projet.
  • Je découvre de nouvelles capacités ou améliorations qui se traduisent par des propositions pour le client.
  • J'ai créé des composants réutilisables. C'était l'un des plus grands avantages car le travail effectué dans le cadre de ce projet aurait pu être réutilisé pour un autre client / projet.
  • Je commence souvent par la solution la plus stupide, codée en dur et farfelue et je la refactorise plus tard une fois que j'apprends plus. Ce dernier moment pourrait être aujourd'hui ou peut-être après plusieurs jours. Je ne veux pas consacrer trop de temps à architectla solution. Cela viendra lors de l'écriture - réécriture du code dans les deux sens.

Dans un monde parfait, les refactorisations devraient être vérifiées par des tests unitaires, des tests d'intégration, etc. S'il n'y en a pas, essayez de les ajouter. De plus, certains IDE peuvent aider beaucoup. J'utilise généralement un plugin qui coûte de l'argent. Je veux passer peu de temps avec les refactorings et être très efficace à ce sujet.

J'ai également introduit des bugs. Je peux voir pourquoi certains QA demandent are you guys doing refactoring? because everything stopped working!C'est le risque que l'équipe doit assumer et nous devons toujours être transparents à ce sujet.

Au fil des ans, j'ai constaté que le refactoring continu améliorait ma productivité. Était beaucoup plus facile d'ajouter de nouvelles fonctionnalités. De plus, les grandes reconstructions ne nécessitaient pas une réécriture complète, comme cela arrivait souvent lorsque la base de code n'était pas adaptée à l'évolution du produit. C'est aussi amusant.


C'est très proche d'un vote négatif - Il n'y a pas de «je» dans «Programmeur informatique». Par «je», vous me «je» (vote négatif) ou voulez-vous dire les développeurs travaillant sur le code maintenant et à l'avenir?
mattnz

Je l'ai remarqué moi aussi, mais je l'ai mis sur le libellé. J'ai également travaillé en tant que développeur solo pendant de nombreuses années et lorsque j'ai écrit cette réponse, j'ai réitéré principalement cette expérience. Je pourrais remplacer Ipar tocependant.
Adrian Iftode

1

Je pense que la question que vous devez vous poser n'est pas tant "Y a-t-il un avantage?" mais "Qui paiera pour ce travail?"

Nous pouvons tous discuter des avantages perçus jusqu'à la fin des jours, mais à moins que vous n'ayez un client qui croit en ces avantages et les valorise suffisamment pour payer les changements, vous ne devriez pas les faire.

C'est trop facile lorsque vous faites partie de l'équipe de développement quelque part pour voir le code et que vous souhaitez le refactoriser pour le rendre «meilleur». Mais mettre une valeur sur un «meilleur code» lorsque le produit fonctionne déjà est vraiment difficile.

Des choses comme «le développement plus rapide de nouvelles fonctionnalités» ne le coupent pas car ce ne sont que des coûts réduits. Vous devez générer des ventes.


1

À mon avis, le refactoring est un bon investissement.

Sinon, vous obtiendrez bientôt des dettes techniques (problèmes non résolus, mauvais code qui ne fonctionne que sous certaines conditions, etc.). Les dettes techniques augmenteront bientôt de plus en plus, jusqu'à ce que le logiciel ne soit plus maintenable.

Mais pour que le refactoring fonctionne, il faut aussi investir dans les tests. Vous devez créer des tests automatisés, idéalement, vous devriez avoir des tests unitaires et des tests d'intégration. Cela vous empêche de casser le code existant.

Si vous devez convaincre votre patron ou vos collègues, vous devriez lire quelques livres sur les méthodes agiles (par exemple SCRUM) et le développement piloté par les tests.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.