¿Cómo borra de forma segura un fragmento de código que parece que nunca se ingresó?

125

Has encontrado un código que parece superfluo, y el compilador no se da cuenta de eso. ¿Qué hace para estar seguro (o lo más cerca que pueda) de que eliminar este código no causará regresión?

Dos ideas vienen a la mente.

  1. "Simplemente" use la deducción en función de si el código parece o no debería ejecutarse. Sin embargo, a veces, esto puede ser un trabajo complejo, que requiere mucho tiempo, un poco riesgoso (su evaluación es propensa a errores) sin un rendimiento empresarial sustancial.

  2. Coloque el registro en esa sección del código y vea con qué frecuencia se ingresa en la práctica. Después de suficientes ejecuciones, debe tener una confianza razonable en que eliminar el código es seguro.

¿Hay mejores ideas o algo así como un enfoque canónico?

    
pregunta Brad Thomas 07.03.2017 - 15:41

20 respuestas

112

En mi mundo de fantasía perfecto donde tengo un 100% de cobertura de prueba unitaria, simplemente lo eliminaría, ejecutaría mis pruebas unitarias y, cuando ninguna prueba se vuelva roja, la cometeré.

Pero desafortunadamente, tengo que despertarme cada mañana y enfrentar la dura realidad donde muchos códigos no tienen pruebas unitarias o cuando están allí no se puede confiar en que cubran todos los casos de borde posibles. Así que consideraría el riesgo / recompensa y llegaría a la conclusión de que simplemente no vale la pena:

  • Recompensa: el código es un poco más fácil de mantener en el futuro.
  • Riesgo: rompa el código en un caso de borde oscuro que no estaba pensando, causo un incidente en el que menos lo espero, y tengo la culpa de ello porque tuve que satisfacer la calidad de mi código OCD y hacer un cambio sin valor empresarial perceptible para cualquier parte interesada que no sea un programador.
respondido por el Philipp 07.03.2017 - 15:52
83

Hay dos mitades en este proceso. Lo primero es confirmar que el código está muerto. El segundo es comprender los costos de equivocarse y asegurarse de que se mitigan adecuadamente.

Muchas respuestas aquí tienen grandes soluciones para la mitad anterior. Las herramientas como los analizadores estáticos son excelentes para identificar códigos muertos. grep puede ser tu amigo en algunos casos. Un paso inusual que a menudo doy es tratar de identificar cuál fue el propósito original del código. Es mucho más fácil argumentar que "X ya no es una característica de nuestro producto, y el segmento de código Y fue diseñado para admitir la característica X" que decir "No veo ningún propósito para el segmento de código Y".

La segunda mitad es un paso clave para romper cualquier estancamiento sobre si debes eliminar el código. Es necesario comprender cuáles son las implicaciones de obtener una respuesta incorrecta. Si la gente va a morir si la respuesta es incorrecta, ¡preste atención! Tal vez sea un buen momento para aceptar que el código cruft se desarrolle con el tiempo y en lugar de eso, intente no escribir más. Si la gente no va a morir, pregúntese qué perdón son sus usuarios. ¿Puede enviarles una revisión si rompe algo y mantiene las relaciones con sus clientes? ¿Tiene un equipo de preguntas y respuestas que un equipo pagado para encontrar problemas como este? Este tipo de preguntas son esenciales para comprender qué tan seguro debe estar antes de presionar la tecla de eliminación.

En los comentarios, rmun señaló una excelente redacción del concepto de comprensión del propósito original del código antes de eliminarlo. La cita ahora se conoce como Chesterton's Fence . Si bien es demasiado grande para ser citado directamente en un comentario, creo que merece ser citado correctamente aquí:

  

En el tema de reformar cosas, a diferencia de deformarlas, hay un principio claro y simple; un principio que probablemente se llamará una paradoja. Existe en tal caso una determinada institución o ley; Digamos, en aras de la simplicidad, una valla o puerta erigida a través de una carretera. El tipo de reformador más moderno se alegra y dice: "No veo el uso de esto; dejémoslo claro ". A lo que el tipo de reformador más inteligente le responderá:" Si no ves su uso, ciertamente no te dejaré limpiarlo. Vete y piensa. Luego, cuando puedas volver y decirme que ves su uso, puedo permitirte que lo destruyas.

    
respondido por el Cort Ammon 07.03.2017 - 17:42
43

También tiendo a grep para la función / nombre de clase en el código, lo que puede dar algunos beneficios adicionales que un analizador de código podría no, como si el nombre se menciona en un comentario o en un archivo de documentación, o guión, por ejemplo. Ejecuto grep en los archivos en el árbol de origen y almaceno el resultado en un archivo; por lo general, el resultado proporciona una información condensada: nombre de archivo / ruta, número de línea y la línea donde se encuentra el nombre, lo que puede dar pistas sobre dónde se llama o menciona la función / clase sin ningún significado semántico (en contraste con un analizador de código) ), e independientemente de las extensiones de archivo. Definitivamente no es la solución definitiva, sino una buena adición al análisis.

    
respondido por el piwi 07.03.2017 - 17:15
30
  • Identifique el código que parece muerto (análisis estático, etc.).
  • Agregue un mensaje de registro para cada invocación del código supuestamente muerto. Es fácil con funciones / métodos; con miembros estáticos como constantes es más complicado. A veces, simplemente puede marcar el código como obsoleto, y el tiempo de ejecución registrará un mensaje automáticamente. Deje el código de otra manera intacto.
    • Registre un mensaje cuando se carga el módulo muerto: la mayoría de los idiomas tienen una forma de ejecutar la inicialización estática en el momento de la carga, que se puede incluir de forma automática.
    • Asegúrese de que su mensaje de registro incluya un seguimiento de pila razonable, de modo que entienda lo que llamó el código supuestamente muerto.
  • Ejecuta el código modificado a través de todas tus suites de prueba. Sus suites de prueba también deben realizar pruebas para momentos especiales, como cruzar una medianoche, un cuarto de año, un año, etc. Mire los registros y actualice su comprensión de lo que está muerto. Tenga en cuenta que las pruebas unitarias pueden probar específicamente el código muerto, mientras que ninguna otra prueba unitaria y ninguna prueba de integración lo tocan.
  • Ejecute el código modificado en producción durante unas pocas semanas. Asegúrese de que todos los procesos periódicos, como los trabajos cronológicos de ETL que se realizan una vez al mes, se ejecuten durante este período.
  • Mira los registros. Todo lo que fue registrado no está realmente muerto. El cierre transitivo del gráfico de llamada sobre el resto del código muerto es también potencialmente no muerto, incluso si no se invocó. Analizarlo Tal vez algunas sucursales estén muertas de manera segura (por ejemplo, trabajando con una API de un servicio ahora extinto), mientras que otras aún no lo están. Tal vez un módulo / clase completo solo se carga para una constante definida en él, y usted puede desusarlo fácilmente.
  • Lo que quede queda muerto, y puede eliminarse.
respondido por el 9000 07.03.2017 - 17:55
16

Además de las respuestas existentes mencionadas, también puede eliminar iterativamente el código de varias versiones.

En la versión inicial, podría hacer una advertencia de desaprobación con el bloque de código aún en funcionamiento. En la versión posterior, puede eliminar el bloque de código pero dejar un mensaje de error que permita a los usuarios que la función esté obsoleta y que ya no esté disponible. En la versión final, eliminarías el bloque de código y cualquier mensaje.

Esto puede ayudar a identificar una funcionalidad imprevista sin advertencia para los usuarios finales. En el mejor de los casos, el código realmente no hace nada y todo lo que sucede es que el código innecesario se mantiene en varias versiones antes de ser eliminado.

    
respondido por el user3196468 08.03.2017 - 04:05
7

Mucha gente está sugiriendo que lo "seguro" que hay que hacer es dejar el código si no puedes probar que no se ha utilizado.

Pero el código no es un activo, es un pasivo.

A menos que pueda explicar por qué es importante y señalar sus pruebas, la alternativa "más segura" podría ser eliminarla.

Si aún no estás seguro, al menos asegúrate de agregar pruebas para ejercitar el código zombie.

    
respondido por el Adrian McCarthy 08.03.2017 - 23:56
6

Puede usar los conmutadores de funciones para cambiar la ruta de ejecución de su software para ignorar completamente el código en cuestión.

De esta manera, puede implementar su cambio de manera segura con el código que no está en uso y con el interruptor desactivado. Si observa algunas fallas importantes relacionadas con el código, vuelva a activar el interruptor e investigue la posible ruta hacia él.

Este enfoque debería darle confianza si no ve problemas durante un período prolongado de tiempo, así como la capacidad de volver a activarlo en vivo sin un despliegue. Sin embargo, una forma aún mejor sería también aplicar registro adicional y cobertura de prueba alrededor del área en cuestión, lo que proporcionará más evidencia de si se usa o no.

Lea más sobre las alternaciones aquí: enlace

    
respondido por el Sash 07.03.2017 - 17:10
6

Análisis estático, por supuesto ... y lo bueno es que no necesita nuevas herramientas; su compilador tiene todas las herramientas de análisis estático que necesita.

Solo cambia el nombre del método (por ejemplo, cambia DoSomething a DoSomethingX ) y luego ejecuta una compilación.

Si tu compilación tiene éxito, el método, obviamente, no está siendo usado por nada. Seguro para eliminar.

Si su compilación falla, aísle la línea de código que la llama y verifique qué declaraciones if rodean la llamada para determinar cómo desencadenarla. Si no puede encontrar ningún posible caso de uso de datos que lo active, puede eliminarlo de manera segura.

Si está realmente preocupado por eliminarlo, considere mantener el código pero marcarlo con un ObsoleteAttribute (o el equivalente en su idioma). Libérelo así para una versión, luego elimine el código después de que no se hayan encontrado problemas en la naturaleza.

    
respondido por el John Wu 08.03.2017 - 05:29
4
  • Comenzaría a usar un analizador de código para verificar si este es un código muerto.
  • Comenzaría a revisar mis pruebas y tratar de alcanzar el código. Si yo soy no puede alcanzar el código dentro de un caso de prueba, podría ser código muerto
  • Quitaría el código y lanzaría una excepción en su lugar. Para una versión, la excepción se activa, y en la segunda versión se puede eliminar la excepción. Para estar seguro, coloque una bandera de emergencia (en Java, una propiedad del sistema) para poder activar el código original, cuando un cliente note la excepción. Por lo tanto, la excepción se puede desactivar y el código original se puede activar en un entorno de producción.
respondido por el Markus Lausberg 07.03.2017 - 15:54
3

Eliminando código inalcanzable

En un lenguaje de tipo estático de principios, siempre debe saber si el código es realmente accesible o no: elimínelo, compile, si no hay un error no se pudo acceder.

Lamentablemente, no todos los idiomas se escriben estáticamente, y no todos los idiomas están tipificados de manera estática. Las cosas que podrían salir mal incluyen (1) reflexión y (2) sobrecarga sin principios.

Si utiliza un lenguaje dinámico o un lenguaje con una reflexión lo suficientemente potente como para que pueda accederse a la pieza de código bajo escrutinio en tiempo de ejecución a través de la reflexión, entonces no puede confiar en el compilador. Tales lenguajes incluyen Python, Ruby o Java.

Si usa un idioma con una sobrecarga sin principios, simplemente eliminando una sobrecarga podría simplemente cambiar la resolución de sobrecarga a la sobrecarga de otra silenciosamente. Algunos de estos lenguajes le permiten programar una advertencia / error en tiempo de compilación asociado con el uso del código, de lo contrario no puede confiar en el compilador. Tales lenguajes incluyen Java (use @Deprecated ) o C ++ (use [[deprecated]] o = delete ).

Por lo tanto, a menos que tenga mucha suerte de trabajar con idiomas estrictos (Rust viene a la mente), puede que realmente se esté disparando a sí mismo al confiar en el compilador. Y, desafortunadamente, las suites de prueba generalmente están incompletas, por lo que tampoco son de mucha ayuda.

Indica la siguiente sección ...

Eliminando código potencialmente no utilizado

Es más probable que se haga referencia al código, sin embargo, sospecha que en la práctica las ramas del código que lo hacen referencia nunca se toman.

En este caso, sin importar el idioma, el código es demostrablemente accesible y solo se puede utilizar la instrumentación en tiempo de ejecución.

En el pasado, he utilizado con éxito un enfoque de 3 fases para eliminar dicho código:

  1. En cada rama sospechosa de NO ser tomada, registre una advertencia.
  2. Después de un ciclo, lanza una excepción / devuelve un error al ingresar el código específico.
  3. Después de otro ciclo, elimina el código.

¿Qué es un ciclo? Es el ciclo de uso del código. Por ejemplo, para una aplicación financiera esperaría un ciclo mensual corto (con los salarios pagados al final del mes) y un ciclo anual largo. En este caso, debe esperar al menos un año para verificar que nunca se emite una advertencia para el inventario de fin de año que podría usar rutas de código que de otra manera nunca se usarían.

Con suerte, la mayoría de las aplicaciones tienen ciclos más cortos.

Aconsejo poner un comentario TODO, con una fecha, aconsejando cuándo pasar al siguiente paso. Y un recordatorio en tu calendario.

    
respondido por el Matthieu M. 08.03.2017 - 11:28
2

Eliminar el código de producción es como limpiar la casa. En el momento en que deseches un objeto del ático, tu esposa te matará al día siguiente por deshacerse del regalo de bienvenida de su vecino, el tercer sobrino de su bisabuela, que murió en 1923.

En serio, después del análisis superficial utilizando varias herramientas que ya todos han mencionado, y después de usar el enfoque de eliminación gradual ya mencionado, tenga en cuenta que puede abandonar la empresa cuando se lleve a cabo la eliminación real. Dejar que el código permanezca y tener su ejecución registrada y asegurada de que cualquier alerta de su ejecución se comunique definitivamente a usted (o a sus sucesores y cesionarios) es esencial.

Si no sigues este enfoque, es probable que tu esposa te mate. Si conserva el código (como todos los recuerdos), puede descansar en el hecho de que la ley de Moore viene a rescatarlo y el costo del espacio en el disco basura utilizado por el código que se siente como una "piedra en mi zapato" reduce cada año y no te arriesgas a convertirte en el centro de múltiples chismes de enfriadores de agua y apariencias extrañas en los pasillos.

PS: Para aclarar en respuesta a un comentario ... La pregunta original es "¿Cómo eliminar de forma segura ...", por supuesto, en mi respuesta se asume el control de versión. Al igual que cavar en la basura para encontrar el valor se asume. Ningún cuerpo con ningún sentido descarta el código y el control de versiones debe ser parte del rigor de cada desarrollador.

El problema es sobre el código que puede ser superfluo. Nunca podemos saber que un fragmento de código es superfluo a menos que podamos garantizar que el 100% de las rutas de ejecución no lo alcancen. Y se supone que este es un software lo suficientemente grande como para que esta garantía sea casi imposible. Y a menos que lea la pregunta de forma incorrecta, la única vez que este hilo de conversación es relevante es en los casos durante la producción en los que se puede llamar al código eliminado y, por lo tanto, tenemos un problema de tiempo de ejecución / producción. El control de versiones no salva a nadie detrás de las fallas de producción, por lo que el comentario sobre "Control de versiones" no es relevante para la pregunta ni para mi punto original, es decir, se debe desaprobar adecuadamente durante un período de tiempo extremadamente largo si realmente tiene que hacerlo, de lo contrario no lo haga. No se preocupe porque el código superfluo agrega un costo relativamente pequeño en la expansión de espacio en disco.

En mi humilde opinión, el comentario es superfluo y es un candidato para su eliminación.

    
respondido por el LMSingh 08.03.2017 - 05:47
1

Tal vez el compilador hace se da cuenta de que, simplemente no es un problema.

Ejecute una compilación con optimizaciones completas del compilador, orientada hacia el tamaño. Luego borre el código sospechoso y ejecute la compilación nuevamente.

Compara los binarios. Si son idénticos, entonces el compilador ha notado y eliminado el código en silencio. Puede eliminarlo de la fuente de forma segura.

Si los binarios son diferentes ... entonces no es concluyente. Podría ser algún otro cambio. Algunos compiladores incluso incluyen la fecha y la hora de la compilación en el binario (¡y tal vez se pueda configurar de forma remota!)

    
respondido por el Emilio M Bumachar 08.03.2017 - 10:46
1

Recientemente, me he encontrado con esta situación exacta, con un método llamado "deleteFoo". En ninguna parte del código base completo se encontró esa cadena distinta de la declaración del método, pero sabiamente escribí una línea de registro en la parte superior del método.

PHP:

public function deleteFoo()
{
    error_log("In deleteFoo()", 3, "/path/to/log");
}

¡Resulta que el código fue utilizado! Algunos métodos AJAX llaman "method: delete, elem = Foo" que luego se concatena y se llama con call_user_func_array () .

¡En caso de duda, inicie sesión! Si ha pasado el tiempo suficiente sin haber completado el registro, considere eliminar el código. Pero incluso si elimina el código, deje el registro en su lugar y un comentario con la fecha para que sea más fácil encontrar el commit en Git para el tipo que tiene que mantenerlo.

    
respondido por el dotancohen 08.03.2017 - 11:03
0

Utilice grep o las herramientas que tenga disponibles primero, puede encontrar referencias al código. (No originalmente mis instrucciones / consejos).

Luego decida si quiere arriesgarse a eliminar el código, o si mi sugerencia podría utilizarse:

Puede modificar la función / bloque de código para escribir que se ha utilizado en un archivo de registro. Si no se registra "nunca" tal mensaje en el archivo de registro, es probable que pueda eliminar el código de registro.

Dos problemas:

  1. Obtención del registro de otros usuarios. Quizás pueda establecer una marca global que bloquee el programa al salir y pida al usuario que le envíe el registro de errores.

  2. Rastreo de la persona que llama. En algunos idiomas de alto nivel (p. ej., Python) puede obtener fácilmente un rastreo. Pero en los idiomas compilados, tendrá que guardar la dirección de retorno en el registro y forzar un volcado de núcleo en la salida (punto 1).
    En C, esto debería ser bastante simple: use un bloque condicional (por ejemplo, #ifdef ... #endif) para usarlo solo cuando sepa que funcionará (y ha probado que sí), y simplemente lea la dirección de retorno de la pila (puede o no requerir lenguaje de ensamblaje en línea).

Guardar los argumentos en el archivo de registro puede o no ser útil.

    
respondido por el Oskar Skog 07.03.2017 - 17:37
0

Si sabe lo que hace el código que lo rodea, pruébelo para ver si se rompe. Esta es la forma más sencilla y económica de refactorizar un fragmento de código en el que está trabajando, y debe hacerse de todos modos para desarrollar cualquier cambio en el código en el que está trabajando en primer lugar.

Si, por otro lado, estás refactorizando un fragmento de código en el que no sabes lo que hace, no lo elimines . Entiéndalo primero, luego refactorícelo si determina que es seguro (y solo si puede determinar que la refactorización sería un uso adecuado de su tiempo).

Ahora, puede haber algunas circunstancias (sé que me he topado con eso) donde el código que está "muerto" en realidad no está conectado a nada . Tales como las bibliotecas de código desarrolladas por un contratista que nunca se implementaron en ningún lugar porque quedaron obsoletas inmediatamente después de que se entregó la aplicación (¿por qué? Sí, tengo un ejemplo específico en mente, ¿cómo lo supo?).

Personalmente terminé eliminando todo este código, pero es un riesgo enorme que no recomiendo tomarlo a la ligera. Dependiendo de la cantidad de código que este cambio pueda potencialmente impacto (porque siempre existe la posibilidad de que haya pasado por alto algo), debe ser extraordinariamente cuidadoso y realizar pruebas unitarias agresivas para determinar si la eliminación de este antiguo código heredado interrumpirá su aplicación.

Ahora que todo esto se dice, probablemente no valga la pena o el tiempo necesario para intentar eliminar un código como ese. No, a menos que se esté convirtiendo en un problema serio con su aplicación (por ejemplo, no poder completar los análisis de seguridad debido a la gran cantidad de aplicaciones ... por qué sí, aún tengo un ejemplo en mente), así que no lo recomendaría a menos que llegue a una situación en la que el código realmente ha comenzado a pudrirse de una manera impactante.

En resumen, si sabe lo que hace el código, pruébelo primero. Si no lo haces, probablemente puedas dejarlo solo. Si sabe que no puede dejarlo solo, dedique su tiempo a probar el cambio agresivamente antes de implementar el cambio.

    
respondido por el Zibbobz 09.03.2017 - 15:44
0

Mi enfoque, que siempre asumí que es el estándar de la industria en este caso, extrañamente no se ha mencionado hasta ahora:

Consigue un segundo par de ojos.

Hay diferentes tipos de "código no utilizado" y, en algunos casos, eliminarlos es apropiado, en otros casos debería refactorizarlo y, en otros casos, huir tan lejos como pueda y nunca mirar hacia atrás. Necesita averiguar cuál es, y para hacerlo, lo mejor es emparejarlo con un segundo: ¡con experiencia! - desarrollador, uno que está muy familiarizado con el código base. Esto reduce significativamente el riesgo de un error innecesario y le permite realizar las mejoras necesarias para mantener el código base en un estado de mantenimiento. Y si no haces esto, en algún momento te quedarás sin desarrolladores que estén muy familiarizados con el código base.

También he visto el enfoque de registro; para ser más exactos, he visto el código de registro: aún más código muerto que se dejó allí durante 10 años. El enfoque de registro es bastante decente si se trata de eliminar funciones completas, pero no si solo se está eliminando una pequeña parte del código muerto.

    
respondido por el Peter 11.03.2017 - 00:59
0

La respuesta simple a tu pregunta es que no puedes eliminar de manera segura el código que no entiendes, lo que incluye no entender cómo se llama. Habrá cierto riesgo.

Tendrá que juzgar la mejor manera de mitigar ese riesgo inevitable. Otros han mencionado la tala. Por supuesto, el registro puede probar que se usa, pero no puede probar que no se usa. Dado que el principal problema con el código muerto es que se mantiene, es posible que pueda agregar un comentario que diga que se sospecha que es un código muerto, y no hacer ningún mantenimiento al respecto.

Si el código está bajo el control de código fuente, siempre puedes averiguar cuándo se agregó y determinar cómo se llamó.

En última instancia, o lo entiendes, lo dejas en paz o te arriesgas.

    
respondido por el jmoreno 11.03.2017 - 15:02
0

En caso de que el desarrollador del código en cuestión aún esté disponible, revise la eliminación del código con él . Además, lee los comentarios en el código .

Obtendrá la explicación correcta, por qué se agregó este código y para qué función sirve. Puede ser fácilmente soporte para el caso de uso específico, o incluso es posible que no tenga suficiente experiencia para juzgar si realmente no es necesario. En caso extremo, uno puede eliminar todas las declaraciones free de un programa de C y no notar nada incorrecto (puede tomar pocos minutos para quedarse sin memoria).

Realmente es una mala cultura cuando el autor del código se sienta a tu lado, pero no ves razón para hablar porque estás seguro de que simplemente escribe un código incorrecto, y el tuyo es perfecto. Y, por supuesto, él solo escribe palabras al azar en los comentarios, no hay necesidad de perder el tiempo leyendo.

Una de las razones más importantes para escribir comentarios en el código es explicar POR QUÉ se ha implementado de esta manera. Puede estar en desacuerdo con la solución, pero debe tener en cuenta el problema.

La discusión con el autor también puede revelar que el código es el remanente histórico de algo eliminado o nunca finalizado, por lo que es seguro eliminarlo.

    
respondido por el h22 11.03.2017 - 19:01
-1

Estoy totalmente de acuerdo con el primer punto de Markus Lausberg: el código definitivamente debe analizarse con la ayuda de herramientas. ¡A los cerebros de los simios no se les puede confiar este tipo de tarea!

La mayoría de los lenguajes de programación estándar se pueden usar en los IDE y cada IDE que vale la pena llamar y que tiene una función de "búsqueda para todos los usos" que es exactamente la herramienta adecuada para este tipo de situación. (Ctrl + Shift + G en Eclipse por ejemplo)

Por supuesto: las herramientas del analizador estático no son perfectas. Uno debe ser consciente de situaciones más complicadas en las que la decisión de que se llame al método en cuestión se produce solo en tiempo de ejecución y no antes (llamadas a través de Reflection, llamadas a funciones "eval" en lenguajes de scripting, etc.).

    
respondido por el Johannes Hahn 07.03.2017 - 17:25
-1

Dos posibilidades:

  1. Tienes un 99% + cobertura de prueba : elimina el código y termina con él.
  2. No tiene una cobertura de prueba confiable: la respuesta es agregar una advertencia de desaprobación . Es decir, agrega un código a ese lugar que hace algo sano (por ejemplo, registre una advertencia en un lugar visible que no entre en conflicto con el uso diario de su aplicación). Luego déjalo cocer a fuego lento durante unos meses / años. Si nunca encontraste que ocurriera algo, reemplaza la desestimación por algo que lanza una excepción. Deja que se quede por un tiempo. Finalmente, elimine todo por completo.
respondido por el AnoE 09.03.2017 - 17:36

Lea otras preguntas en las etiquetas