En mi equipo hemos estado limpiando muchas cosas viejas en un gran proyecto monolítico (clases completas, métodos, etc.).
Durante esas tareas de limpieza, me preguntaba si hay algún tipo de anotación o biblioteca más elegante que la habitual @Deprecated
. Esto @FancyDeprecated
debería evitar que la compilación del proyecto tenga éxito si no ha limpiado el código antiguo no utilizado después de que haya pasado una fecha en particular.
He estado buscando en Internet y no encontré nada que tenga las capacidades que se describen a continuación:
- debe ser una anotación, o algo similar, para colocar en el código que está destinado a eliminar antes de una fecha en particular
- antes de esa fecha, el código se compilará y todo funcionará normalmente
- después de esa fecha, el código no se compilará y le dará un mensaje advirtiéndole sobre el problema
Creo que estoy buscando un unicornio ... ¿Existe alguna tecnología similar para algún lenguaje de programa?
Como plan, estoy pensando en la posibilidad de hacer la magia con algunas pruebas unitarias del código que se pretende eliminar que comienzan a fallar en la "fecha límite". ¿Qué piensas sobre esto? ¿Alguna mejor idea?
fuente
Respuestas:
No creo que esta sea una característica útil cuando realmente prohíbe la compilación. Cuando al 01/06/2018 no se compilarán grandes partes del código que compiló el día anterior, su equipo eliminará rápidamente esa anotación nuevamente, el código se limpió o no.
Sin embargo, puede agregar alguna anotación personalizada al código como
y crea una pequeña herramienta para escanear esas anotaciones. (Un trazador de líneas simple en grep lo hará, si no desea utilizar la reflexión). En otros lenguajes además de Java, se puede utilizar un comentario estandarizado adecuado para "grepping" o una definición de preprocesador.
Luego ejecute esa herramienta poco antes o después de la fecha en particular, y si aún encuentra esa anotación, recuerde al equipo que limpie esas partes del código con urgencia.
fuente
Esto constituiría una característica conocida como bomba de tiempo . NO CREES BOMBAS DE TIEMPO.
El código, no importa cuán bien lo estructura y documenta, se convertirá en una caja negra casi mítica mal entendida si vive más allá de cierta edad. Lo último que alguien necesita en el futuro es otro extraño modo de falla que los tome por sorpresa, en el peor momento posible y sin un remedio obvio. No hay absolutamente ninguna excusa para producir intencionalmente tal problema.
Míralo de esta manera: si estás organizado y conoces la base de tu código lo suficiente como para que te preocupe la obsolescencia y la sigas, entonces no necesitas un mecanismo dentro del código que te lo recuerde. Si no lo está, es probable que tampoco esté actualizado sobre otros aspectos de la base del código y que probablemente no pueda responder a la alarma de manera oportuna y correcta. En otras palabras, las bombas de tiempo no sirven para nada. ¡Solo di no!
fuente
En C # usaría el
ObsoleteAttribute
de la siguiente manera:La idea aquí es hacer un cambio radical lo más sencillo posible para los usuarios afectados, y garantizar que puedan seguir utilizando la función para al menos una versión de la biblioteca.
fuente
libfoo.so.2
ylibfoo.so.3
pueden coexistir entre sí muy bien, su aguas abajo puede seguir usando la antigua biblioteca hasta que son activados por encima.Has entendido mal lo que significa "obsoleto". En desuso significa:
Diccionarios Oxford
Por definición, una característica obsoleta aún se compilará.
Está buscando eliminar la función en una fecha específica. Esta bien. La forma de hacerlo es eliminarlo en esa fecha .
Hasta entonces, márquelo como obsoleto, obsoleto o como lo llame su lenguaje de programación. En el mensaje, incluya la fecha en que se eliminará y lo que lo reemplaza. Esto generará advertencias, lo que indica que otros desarrolladores deben evitar el uso nuevo y reemplazar el uso antiguo siempre que sea posible. Esos desarrolladores lo cumplirán o lo ignorarán, y alguien tendrá que lidiar con las consecuencias de eso cuando se elimine. (Dependiendo de la situación, puede ser usted o los desarrolladores que lo usen).
fuente
No olvide que debe conservar la capacidad de compilar y depurar versiones anteriores del código para admitir versiones del software que ya se han lanzado. El sabotaje de una compilación después de una fecha determinada significa que también corre el riesgo de evitar realizar trabajos de mantenimiento y soporte legítimos en el futuro.
Además, parece una solución trivial para retrasar el reloj de mi máquina un año o dos antes de compilar.
Recuerde, "obsoleto" es una advertencia de que algo desaparecerá en el futuro. Cuando desee evitar a la fuerza que las personas usen esa API, simplemente elimine el código asociado . No tiene sentido dejar código en la base de código si algún mecanismo lo deja inutilizable. Eliminar el código le proporciona las comprobaciones en tiempo de compilación que está buscando y no tiene una solución trivial.
Editar: veo que se refiere al "código antiguo no utilizado" en su pregunta. Si el código realmente no se usa , no tiene sentido desaprobarlo. Solo bórralo.
fuente
Nunca antes había visto una característica así: una anotación que comienza a tener efecto después de una fecha en particular.
El
@Deprecated
puede ser suficiente, sin embargo. Capture advertencias en CI y haga que se niegue a aceptar la compilación si hay alguna presente. Esto cambia la responsabilidad del compilador a su canal de compilación, pero tiene la ventaja de que puede (semi) modificar fácilmente el canal de compilación agregando pasos adicionales.Tenga en cuenta que esta respuesta no resuelve completamente su problema (por ejemplo, las compilaciones locales en las máquinas de los desarrolladores seguirían teniendo éxito, aunque con advertencias) y asume que tiene una tubería CI configurada y en funcionamiento.
fuente
Estás buscando calendarios o listas de tareas pendientes .
Otra alternativa es usar advertencias de compilación personalizadas o mensajes de compilación, si logra tener pocas o ninguna advertencia en su base de código. Si tiene demasiadas advertencias, deberá dedicar un esfuerzo adicional (¿aproximadamente 15 minutos?) Y deberá recoger la advertencia del compilador en el informe de compilación que entrega su integración continua en cada compilación.
Los recordatorios de que el código necesita ser reparado son buenos y necesarios. A veces, estos recordatorios tienen plazos estrictos en el mundo real, por lo que también puede ser necesario ponerlos en un temporizador.
El objetivo es recordar continuamente a las personas que el problema existe y necesita ser solucionado dentro de un período de tiempo dado, una característica que simplemente rompe la compilación en un momento específico no solo no lo hace, sino que esa característica es en sí misma un problema que necesita arreglarse dentro de un plazo determinado.
fuente
Una forma de pensar en esto es lo que quieres decir con hora / fecha . Las computadoras no saben cuáles son estos conceptos: tienen que ser programados de alguna manera. Es bastante común representar tiempos en el formato UNIX de "segundos desde la época", y es común alimentar un valor particular en un programa a través de llamadas al sistema operativo. Sin embargo, no importa cuán común sea este uso, es importante tener en cuenta que no es el momento "real": es solo una representación lógica.
Como otros han señalado, si hiciste una "fecha límite" usando este mecanismo, es trivial alimentarlo en un momento diferente y romper esa "fecha límite". Lo mismo ocurre con mecanismos más elaborados, como solicitar un servidor NTP (incluso a través de una conexión "segura", ya que podemos sustituir nuestros propios certificados, autoridades de certificación o incluso parchear las bibliotecas de cifrado). Al principio puede parecer que esas personas tienen la culpa de trabajar alrededor de su mecanismo, pero puede ser el caso de que se haga automáticamente y por buenas razones . Por ejemplo, es una buena idea tener compilaciones reproducibles , y las herramientas para ayudarlo podrían restablecer / interceptar automáticamente tales llamadas de sistema no deterministas. libfaketime hace exactamente eso,establece todas las marcas de tiempo del archivo en
1970-01-01 00:00:01
, la función de grabación / reproducción de Qemu falsifica toda interacción de hardware, etc.Esto es similar a la ley de Goodhart : si hace que el comportamiento de un programa dependa del tiempo lógico, entonces el tiempo lógico deja de ser una buena medida del tiempo "real". En otras palabras, las personas generalmente no se meterán con el reloj del sistema, pero lo harán si les das una razón.
Hay otras representaciones lógicas del tiempo: una de ellas es la versión del software (ya sea su aplicación o alguna dependencia). Esta es una representación más deseable para una "fecha límite" que, por ejemplo, el tiempo UNIX, ya que es más específica para lo que le interesa (cambiar conjuntos de características / API) y, por lo tanto, es menos probable que pisotee las preocupaciones ortogonales (por ejemplo, jugar con el tiempo UNIX para evitar su fecha límite podría terminar rompiendo archivos de registro, trabajos cron, cachés, etc.).
Como han dicho otros, si controlas la biblioteca y quieres "impulsar" este cambio, puedes impulsar una nueva versión que desaproveche las características (provocando advertencias, para ayudar a los consumidores a encontrar y actualizar su uso), entonces otra nueva versión que elimine el características por completo. Si lo desea, puede publicarlos inmediatamente uno después del otro, ya que (de nuevo) las versiones son simplemente una representación lógica del tiempo, no necesitan estar relacionadas con el tiempo "real". Las versiones semánticas pueden ayudar aquí.
El modelo alternativo es "tirar" del cambio. Esto es como su "plan B": agregue una prueba a la aplicación consumidora, que verifica que la versión de esta dependencia sea al menos el nuevo valor. Como de costumbre, rojo / verde / refactor para propagar este cambio a través de la base de código. Esto puede ser más apropiado si la funcionalidad no es "mala" o "incorrecta", sino simplemente "una mala opción para este caso de uso".
Una pregunta importante con el enfoque "pull" es si la versión de dependencia cuenta o no como una "unidad" ( de funcionalidad ) y, por lo tanto, merece ser probada; o si se trata solo de un detalle de implementación "privada", que solo debe ejercerse como parte de las pruebas de unidad ( de funcionalidad ) reales . Yo diría: si la distinción entre las versiones de la dependencia realmente cuenta como una característica de su aplicación, entonces haga la prueba (por ejemplo, verifique que la versión de Python sea> = 3.x). Si no, entonces noagregue la prueba (ya que será frágil, poco informativa y demasiado restrictiva); si controlas la biblioteca, ve por la ruta "push". Si no controla la biblioteca, simplemente use la versión que se le proporcione: si sus pruebas pasan, entonces no vale la pena restringirse; si no pasan, ¡esa es tu "fecha límite" allí mismo!
Existe otro enfoque, si desea desalentar ciertos usos de las características de una dependencia (por ejemplo, llamar a ciertas funciones que no funcionan bien con el resto de su código), especialmente si no controla la dependencia: prohíba sus estándares de codificación / desaliente el uso de estas funciones y agregue comprobaciones para ellas a su linter.
Cada uno de estos será aplicable en diferentes circunstancias.
fuente
Usted administra esto a nivel de paquete o biblioteca. Usted controla un paquete y controla su visibilidad. Eres libre de retraer la visibilidad. He visto esto internamente en grandes empresas y solo tiene sentido en culturas que respetan la propiedad de los paquetes, incluso si los paquetes son de código abierto o de uso gratuito.
Esto siempre es complicado porque los equipos de clientes simplemente no quieren cambiar nada, por lo que a menudo necesita algunas rondas de la lista blanca solo mientras trabaja con los clientes específicos para acordar una fecha límite para migrar, posiblemente ofreciéndoles soporte.
fuente
Un requisito es introducir una noción de tiempo en la compilación. En C, C ++, u otros lenguajes / construir sistemas que utilizan un preprocesador C-como 1 , se podría introducir un sello de tiempo a través define para el preprocesador en tiempo de compilación:
CPPFLAGS=-DTIMESTAMP()=$(date '+%s')
. Esto probablemente sucedería en un archivo MAKE.En el código, uno compararía ese token y causaría un error si se acaba el tiempo. Tenga en cuenta que el uso de una macro de función detecta el caso que alguien no definió
TIMESTAMP
.Alternativamente, uno podría simplemente "definir" el código en cuestión cuando haya llegado el momento. Eso permitiría compilar el programa, siempre que nadie lo use. Digamos que tenemos un encabezado que define una api, "api.h", y no permitimos llamar
old()
después de un cierto tiempo:Una construcción similar probablemente eliminaría
old()
el cuerpo de la función de algún archivo fuente.Por supuesto, esto no es infalible; uno simplemente puede definir un viejo
TIMESTAMP
en caso de la construcción de emergencia del viernes por la noche mencionada en otra parte. Pero eso es, creo, bastante ventajoso.Obviamente, esto solo funciona cuando la biblioteca se vuelve a compilar; después de eso, el código obsoleto simplemente ya no existe en la biblioteca. Sin embargo, no evitaría que el código del cliente se vincule a binarios obsoletos.
1 C # solo admite la definición simple de símbolos de preprocesador, sin valores numéricos, lo que hace que esta estrategia no sea viable.
fuente
TIMESTAMP
a que se recompile todo el código que se usa en cada compilación. También incapacitará herramientas comoccache
. Esto significa que el tiempo de compilación típico para compilaciones incrementales aumentará significativamente dependiendo de la cantidad de código base afectada por las características desaprobadas de esta manera.TIMESTAMP
valor por, digamos, 86400, para obtener una granularidad diaria y, por lo tanto, menos recompilaciones.En Visual Studio, puede configurar una secuencia de comandos previa a la compilación que arroje un error después de una fecha determinada. Esto evitará la compilación. Aquí hay un script que arroja un error a partir del 12 de marzo de 2018 ( tomado de aquí ):
Asegúrese de leer las otras respuestas en esta página antes de usar este script.
fuente
Entiendo el objetivo de lo que estás tratando de hacer. Pero como otros han mencionado, el sistema / compilador de compilación probablemente no sea el lugar adecuado para hacer cumplir esto. Sugeriría que la capa más natural para hacer cumplir esta política es la SCM o las variables de entorno.
Si hace lo último, básicamente agregue un indicador de característica que marque una ejecución previa a la eliminación. Cada vez que construya la clase en desuso o llame a un método en desuso, verifique el indicador de función. Simplemente defina una única función estática
assertPreDeprecated()
y agregue esto a cada ruta de código obsoleta. Si está configurado, ignore las llamadas de aserción. Si no es lanzar una excepción. Una vez que la fecha pase, desactive el indicador de función en el entorno de tiempo de ejecución. Cualquier llamada obsoleta al código que permanezca aparecerá en los registros de tiempo de ejecución.Para una solución basada en SCM, supondré que estás usando git y git-flow. (Si no, la lógica es fácilmente adaptable a otros VCS). Crea una nueva sucursal
postDeprecated
. En esa rama, elimine todo el código obsoleto y comience a trabajar para eliminar cualquier referencia hasta que se compile. Cualquier cambio normal continúa haciendo en lamaster
rama. Mantenga fusionando cualquier cambio que no son obsoletas relacionadas con el código demaster
nuevo enpostDeprecated
reducir al mínimo los retos de integración.Después de que finalice la fecha de desaprobación, cree una nueva
preDeprecated
rama desdemaster
. Luego fusionarsepostDeprecated
nuevamentemaster
. Suponiendo que su lanzamiento se salga de lamaster
sucursal, ahora debería estar utilizando la sucursal posterior a la desaprobación después de la fecha. Si hay una emergencia, o no puede entregar resultados a tiempo, siempre puede retrocederpreDeprecated
y hacer los cambios necesarios en esa rama.fuente