Dans mon équipe, nous avons nettoyé beaucoup de vieux matériaux dans le cadre d'un grand projet monolithique (cours entiers, méthodes, etc.).
Pendant ces tâches de nettoyage, je me demandais s’il existait une sorte d’annotation ou de bibliothèque plus sophistiquée que la normale @Deprecated
. Cela @FancyDeprecated
devrait empêcher la génération du projet de réussir si vous n'avez pas nettoyé l'ancien code inutilisé après une date donnée.
J'ai effectué une recherche sur Internet et je n'ai rien trouvé qui présente les fonctionnalités décrites ci-dessous:
- devrait être une annotation, ou quelque chose de similaire, à placer dans le code que vous êtes censé supprimer avant une date donnée
- avant cette date, le code sera compilé et tout fonctionnera normalement
- après cette date, le code ne sera pas compilé et il vous enverra un message vous avertissant du problème
Je pense que je recherche une licorne ... Existe-t-il une technologie similaire pour toutes les langues de programme?
En tant que plan BI, je pense à la possibilité de faire de la magie avec certains tests unitaires du code destiné à être supprimé qui commencent à échouer à la "date limite". Que pensez-vous de ceci? Une meilleure idée?
Réponses:
Je ne pense pas que ce soit une fonctionnalité utile quand elle interdit vraiment la compilation. Lorsque, au 01/06/2018, une grande partie du code ne compilera pas celle compilée la veille, votre équipe supprimera rapidement cette annotation, que le code soit nettoyé ou non.
Cependant, vous pouvez ajouter des annotations personnalisées au code telles que
et construisez un petit outil pour rechercher ces annotations. (Un simple support dans grep le fera si vous ne voulez pas utiliser la réflexion). Dans d'autres langages que Java, vous pouvez utiliser un commentaire standardisé adapté à "grepping" ou une définition de pré-processeur.
Exécutez ensuite cet outil peu de temps avant ou après la date donnée et, s'il trouve toujours cette annotation, rappelez à l'équipe de nettoyer ces parties de code de toute urgence.
la source
Cela constituerait une caractéristique connue sous le nom de bombe à retardement . NE CRÉEZ PAS DE BOMBES TEMPORELLES.
Le code, quelle que soit votre structure et votre documentation, deviendra une boîte noire quasi mythique mal comprise s'il survit au-delà d'un certain âge. La dernière chose dont tout le monde a besoin dans le futur est encore un autre mode de défaillance étrange qui les surprend complètement, au plus mauvais moment et sans solution évidente. Il n'y a absolument aucune excuse pour produire intentionnellement un tel problème.
Examinez la situation de la manière suivante: si vous êtes suffisamment organisé et que vous connaissez suffisamment votre base de code pour vous préoccuper de votre obsolescence, vous n'avez pas besoin d'un mécanisme dans le code pour vous le rappeler. Sinon, vous risquez également de ne pas être au courant des autres aspects de la base de code et de ne pas être en mesure de réagir correctement et correctement à l'alarme. En d'autres termes, les bombes à retardement ne servent à rien. Dis juste non!
la source
En C #, vous utiliseriez le
ObsoleteAttribute
comme suit:L'idée ici est de faire un changement radical aussi indolore que possible pour les utilisateurs concernés et de s'assurer qu'ils peuvent continuer à utiliser la fonctionnalité pour au moins une version de la bibliothèque.
la source
libfoo.so.2
etlibfoo.so.3
peuvent parfaitement coexister les uns avec les autres, votre aval peut continuer à utiliser l'ancienne bibliothèque jusqu'à ce qu'ils soient basculés.Vous avez mal compris ce que signifie "déconseillé". Obsolète signifie:
Dictionnaires Oxford
Par définition, une fonctionnalité obsolète sera toujours compilée.
Vous souhaitez supprimer la fonctionnalité à une date spécifique. C'est très bien. Comme vous le faites, vous le supprimez à cette date .
Jusque-là, marquez comme obsolète, obsolète ou quelle que soit l’appel de votre langage de programmation. Dans le message, incluez la date à laquelle il sera supprimé et la chose qui le remplace. Cela générera des avertissements indiquant que les autres développeurs doivent éviter les nouvelles utilisations et remplacer les anciennes utilisations dans la mesure du possible. Ces développeurs vont s'y conformer ou l'ignorer, et quelqu'un devra faire face aux conséquences de cette suppression. (Selon la situation, ce peut être vous ou les développeurs qui l'utilisent.)
la source
N'oubliez pas que vous devez conserver la possibilité de créer et de déboguer des versions antérieures du code afin de prendre en charge les versions du logiciel déjà publiées. Saboter une construction après une certaine date signifie que vous risquez également de vous empêcher d'effectuer des travaux de maintenance et de support légitimes à l'avenir.
En outre, il semble être une solution de contournement triviale de remettre l’horloge de ma machine un an ou deux avant la compilation.
Rappelez-vous, "obsolète" est un avertissement que quelque chose va disparaître dans le futur. Lorsque vous souhaitez empêcher de manière forcée les personnes d'utiliser cette API, supprimez simplement le code associé . Il ne sert à rien de laisser du code dans la base de code si un mécanisme le rend inutilisable. La suppression du code vous donne les vérifications à la compilation que vous recherchez, sans solution de rechange simple.
Edit: Je vois que vous faites référence à "l'ancien code inutilisé" dans votre question. Si le code est vraiment inutilisé , il est inutile de le déconseiller. Supprimez-le simplement.
la source
Je n'avais jamais vu une telle caractéristique auparavant - une annotation qui prend effet après une date donnée.
Le
@Deprecated
peut être suffisant, cependant. Attrapez les avertissements dans CI et faites-le refuser d'accepter la construction s'il en existe. Cela déplace la responsabilité du compilateur vers votre pipeline de construction, mais présente l'avantage de pouvoir modifier (semi) facilement le pipeline de construction en ajoutant des étapes supplémentaires.Notez que cette réponse ne résout pas complètement votre problème (par exemple, les constructions locales sur les machines des développeurs continueraient à fonctionner, bien qu'avec des avertissements) et suppose que vous avez un pipeline CI configuré et en cours d'exécution.
la source
Vous recherchez des calendriers ou des listes de tâches .
Une autre alternative consiste à utiliser des avertissements personnalisés du compilateur ou des messages du compilateur, si vous réussissez à avoir très peu d’avertissements dans votre code. Si vous avez trop d'avertissements, vous devrez déployer des efforts supplémentaires (environ 15 minutes?) Et prendre l'avertissement du compilateur dans le rapport de construction fourni par votre intégration continue pour chaque construction.
Les rappels que le code doit être corrigé sont bons et nécessaires. Parfois, ces rappels ont des échéances strictes dans le monde réel. Il peut donc être nécessaire de les programmer.
L’objectif est de rappeler continuellement aux gens que le problème existe et qu’il doit être corrigé avec un laps de temps donné - une fonctionnalité qui rompt simplement la construction à un moment donné non seulement ne le fait pas, mais est également un problème qui doit être résolu. être fixé avec un laps de temps donné.
la source
Une façon de penser à cela est ce que vous entendez par heure / date ? Les ordinateurs ne savent pas ce que sont ces concepts: ils doivent être programmés d'une manière ou d'une autre. Il est assez courant de représenter des durées au format UNIX "secondes depuis l'époque" et d'introduire une valeur particulière dans un programme via des appels de système d'exploitation. Cependant, quelle que soit la fréquence de cet usage, il est important de garder à l'esprit que ce n'est pas le moment "réel": c'est simplement une représentation logique.
Comme d'autres l'ont fait remarquer, si vous définissiez une "échéance" à l'aide de ce mécanisme, il est trivial d'alimenter une heure différente et de dépasser cette "échéance". Il en va de même pour les mécanismes plus élaborés tels que la demande à un serveur NTP (même sur une connexion "sécurisée", puisque nous pouvons substituer nos propres certificats, autorités de certification ou même appliquer des correctifs aux bibliothèques de chiffrement). Au début, il peut sembler que ces personnes sont responsables du travail effectué autour de votre mécanisme, mais il se peut que cela se fasse automatiquement et pour de bonnes raisons . Par exemple, il est judicieux de disposer de versions reproductibles et des outils d'aide à cette fin pourraient réinitialiser / intercepter automatiquement ces appels système non déterministes. libfaketime fait exactement cela,définit l'horodatage de tous les fichiers sur
1970-01-01 00:00:01
, la fonction d' enregistrement / lecture de Qemu simule toute interaction matérielle, etc.Ceci est similaire à la loi de Goodhart : si le comportement d'un programme dépend du temps logique, le temps logique cesse alors d'être une bonne mesure du temps "réel". En d'autres termes, les gens ne vont généralement pas jouer avec l'horloge système, mais ils le feront si vous leur en donnez la raison.
Il existe d'autres représentations logiques du temps: l'une d'entre elles est la version du logiciel (soit votre application, soit une dépendance). C’est une représentation plus souhaitable pour une "échéance" que, par exemple, l’heure UNIX, car elle est plus spécifique à ce qui vous intéresse (modification des ensembles de fonctionnalités / API) et est donc moins susceptible de piétiner des préoccupations orthogonales contourner votre échéance pourrait casser des fichiers journaux, des tâches cron, des caches, etc.).
Comme d’autres l’ont dit, si vous contrôlez la bibliothèque et souhaitez "pousser" cette modification, vous pouvez proposer une nouvelle version qui déprécie les fonctionnalités caractéristiques entièrement. Vous pouvez les publier immédiatement les uns après les autres si vous le souhaitez, car (encore) les versions ne sont que des représentations logiques du temps, elles n'ont pas besoin d'être liées au temps "réel". La gestion sémantique peut aider ici.
Le modèle alternatif consiste à "tirer" le changement. Cela ressemble à votre "plan B": ajoutez un test à l'application consommatrice, qui vérifie que la version de cette dépendance est au moins la nouvelle valeur. Comme d'habitude, red / green / refactor pour propager ce changement dans la base de code. Cela peut être plus approprié si la fonctionnalité n'est pas "mauvaise" ou "mauvaise", mais simplement "un mauvais ajustement pour ce cas d'utilisation".
Une question importante avec l'approche "pull" est de savoir si la version de dépendance compte ou non comme une "unité" ( de fonctionnalité ) et mérite donc d'être testée; ou s'il s'agit simplement d'un détail d'implémentation "privé", qui ne doit être exercé que dans le cadre de tests unitaires ( de fonctionnalité ) réels . Je dirais: si la distinction entre les versions de la dépendance compte vraiment comme une fonctionnalité de votre application, faites le test (par exemple, en vérifiant que la version Python est> = 3.x). Si non, alors ne le faites pasajouter le test (car il sera fragile, peu informatif et trop restrictif); si vous contrôlez la bibliothèque, descendez ensuite la route "push". Si vous ne contrôlez pas la bibliothèque, utilisez simplement la version fournie: si vos tests réussissent, inutile de vous limiter; S'ils ne passent pas, c'est votre "date limite"!
Il existe une autre approche, si vous souhaitez décourager certaines utilisations des fonctionnalités d'une dépendance (par exemple, appeler certaines fonctions qui ne fonctionnent pas bien avec le reste de votre code), en particulier si vous ne contrôlez pas la dépendance: interdisez vos normes de codage. / décourage l'utilisation de ces fonctionnalités et ajoute des contrôles à votre linter.
Chacun de ceux-ci sera applicable dans des circonstances différentes.
la source
Vous gérez cela au niveau du paquet ou de la bibliothèque. Vous contrôlez un package et sa visibilité. Vous êtes libre de retirer la visibilité. J'ai vu cela en interne dans de grandes entreprises et cela n'a de sens que dans des cultures qui respectent la propriété des packages, même si ces derniers sont open source ou gratuits.
Cela est toujours gênant parce que les équipes de clients ne veulent tout simplement rien changer. Vous avez donc souvent besoin de listes blanches, car vous travaillez avec des clients spécifiques pour convenir d'un délai de migration, leur offrant éventuellement une assistance.
la source
Une exigence est d'introduire une notion de temps dans la construction. En C, C ++ ou d' autres langages / construire des systèmes qui utilisent un type C préprocesseur 1 , on pourrait introduire un horodatage par définit pour le préprocesseur à moment de la construction:
CPPFLAGS=-DTIMESTAMP()=$(date '+%s')
. Cela se produirait probablement dans un fichier makefile.Dans le code, on pourrait comparer ce jeton et provoquer une erreur si le temps est écoulé. Notez que l'utilisation d'une macro de fonction intercepte le cas que quelqu'un n'a pas défini
TIMESTAMP
.Alternativement, on pourrait simplement "définir" le code en question lorsque le moment sera venu. Cela permettrait au programme de compiler, à condition que personne ne l'utilise. Dites, nous avons un en-tête définissant une api, "api.h", et nous ne permettons pas d'appeler
old()
après un certain temps:Une construction similaire éliminerait probablement
old()
le corps de la fonction d'un fichier source.Bien sûr, cela n’est pas infaillible; on peut simplement définir une ancienne
TIMESTAMP
en cas de construction d'urgence du vendredi soir mentionnée ailleurs. Mais c’est, je pense, plutôt avantageux.Cela ne fonctionne évidemment que lorsque la bibliothèque est recompilée - après quoi le code obsolète n’existe tout simplement plus dans la bibliothèque. Cela n'empêcherait cependant pas le code client de créer un lien vers des fichiers binaires obsolètes.
1 C # ne prend en charge que la simple définition des symboles du préprocesseur, sans valeur numérique, ce qui rend cette stratégie non viable.
la source
TIMESTAMP
à être recompilé à chaque génération. Il va également neutraliser des outils commeccache
. Cela signifie que le temps de compilation typique pour les générations incrémentielles augmentera considérablement en fonction de la quantité de base de code affectée par les fonctionnalités obsolètes de cette manière.TIMESTAMP
valeur par 86400, par exemple, pour obtenir une granularité quotidienne et par conséquent moins de recompilations.Dans Visual Studio, vous pouvez configurer un script de pré-génération qui génère une erreur après une certaine date. Cela empêchera la compilation. Voici un script qui génère une erreur à partir du 12 mars 2018 ( pris ici ):
Assurez-vous de lire les autres réponses sur cette page avant d'utiliser ce script.
la source
Je comprends l'objectif de ce que vous essayez de faire. Mais comme d'autres l'ont mentionné, le système de compilation / compilateur n'est probablement pas le bon endroit pour appliquer cela. Je suggérerais que la couche la plus naturelle pour appliquer cette stratégie soit les variables SCM ou d'environnement.
Si vous effectuez cette dernière opération, ajoutez en gros un indicateur de fonctionnalité qui marque une exécution antérieure à la dépréciation. Vérifiez chaque fois que vous construisez la classe obsolète ou appelez une méthode déconseillée. Définissez simplement une fonction statique unique
assertPreDeprecated()
et ajoutez-la à chaque chemin de code obsolète. Si cette option est définie, ignorez les appels d’assertion. Si ce n'est pas jeter une exception. Une fois la date passée, désactivez l'indicateur de fonctionnalité dans l'environnement d'exécution. Tous les appels obsolètes en attente au code seront affichés dans les journaux d'exécution.Pour une solution basée sur SCM, je suppose que vous utilisez git et git-flow. (Si ce n'est pas le cas, la logique est facilement adaptable à d'autres VCS). Créer une nouvelle branche
postDeprecated
. Dans cette branche, supprimez tout le code obsolète et commencez à supprimer toutes les références jusqu'à la compilation. Toutes les modifications normales continuent à apporter à lamaster
branche. Gardez en fusionnant les modifications correspondantes du code non dépréciées àmaster
nouveau danspostDeprecated
de minimiser les problèmes d'intégration.Une fois la date d'exprécision terminée, créez une nouvelle
preDeprecated
branche à partir demaster
. Puis fusionnezpostDeprecated
dansmaster
. En supposant que votre version soit sortie de lamaster
branche, vous devriez maintenant utiliser la branche post-déconseillée après la date. En cas d'urgence ou si vous ne pouvez pas fournir les résultats à temps, vous pouvez toujours revenir en arrièrepreDeprecated
et apporter les modifications nécessaires sur cette branche.la source