Realmente no escribo proyectos grandes. No mantengo una gran base de datos ni trato con millones de líneas de código.
Mi código es principalmente material de tipo "script" (cosas para probar funciones matemáticas o para simular algo), "programación científica". Los programas más largos en los que he trabajado hasta ahora son un par de cientos de líneas de código, y la mayoría de los programas en los que trabajo son alrededor de 150.
Mi código también es basura. Me di cuenta de esto el otro día cuando estaba tratando de encontrar un archivo que escribí hace un tiempo, pero que probablemente sobrescribí y que no uso el control de versiones, lo que probablemente hace que una gran cantidad de ustedes se encojan de agonía ante mi estupidez.
El estilo de mi código es complicado y está lleno de comentarios obsoletos que indican formas alternativas de hacer algo o con líneas de código copiadas. Si bien los nombres de las variables siempre comienzan muy bien y son descriptivos, a medida que agrego o cambio cosas según, por ejemplo, algo nuevo que alguien quiere probar, el código se superpone en la parte superior y se sobrescribe y porque siento que esto debería probarse rápidamente ahora que tengo un marco de trabajo, empiezo a usar nombres de variables malísimos y el archivo va al bote.
En el proyecto en el que estoy trabajando ahora, estoy en la fase en la que todo esto vuelve para morderme a lo grande. Pero el problema es (aparte de usar el control de versiones y hacer un nuevo archivo para cada nueva iteración y grabarlo todo en un archivo de texto en algún lugar, lo que probablemente ayudará a la situación dramáticamente) Realmente no sé cómo proceder para mejorar Mi estilo de codificación real.
¿Son necesarias las pruebas unitarias para escribir piezas de código más pequeñas? ¿Qué tal OOP? ¿Qué tipo de enfoques son buenos para escribir un código bueno y limpio rápidamente al hacer "programación científica" en lugar de trabajar en proyectos más grandes?
Hago estas preguntas porque a menudo, la programación en sí misma no es súper compleja. Se trata más de matemáticas o ciencias que estoy probando o investigando con la programación. Por ejemplo, ¿es necesaria una clase cuando dos variables y una función probablemente podrían ocuparse de ella? (Tenga en cuenta que, en general, también se trata de situaciones en las que se prefiere que la velocidad del programa sea más rápida: cuando ejecuta más de 25,000,000 pasos de una simulación, quiere que sea así).
Quizás esto es demasiado amplio, y si es así, me disculpo, pero al mirar los libros de programación, a menudo parecen abordarse en proyectos más grandes. Mi código no necesita OOP, y ya es bastante corto, así que no es como "¡oh, pero el archivo se reducirá en mil líneas si lo hacemos!" Quiero saber cómo "comenzar de nuevo" y programar limpiamente en estos proyectos más pequeños y rápidos.
Me complacería proporcionar detalles más específicos, pero cuanto más general sea el consejo, más útil, creo. Estoy programando en Python 3.
Alguien sugirió un duplicado. Permítanme aclarar que no estoy hablando de ignorar directamente los estándares de programación estándar. Claramente, hay una razón por la que existen esos estándares. Pero, por otro lado, ¿tiene sentido escribir código que diga OOP cuando algunas cosas estándar podrían haberse hecho, hubieran sido mucho más rápidas de escribir y hubieran tenido un nivel similar de legibilidad debido a la brevedad del ¿programa?
Hay excepciones Además, probablemente haya estándares para la programación científica más allá de los estándares simples. También estoy preguntando por eso. No se trata de si los estándares de codificación normales deben ignorarse al escribir código científico, ¡se trata de escribir un código científico limpio!
Actualizar
Solo pensé que agregaría una especie de actualización de "no una semana después". Todos sus consejos fueron extremadamente útiles. Ahora estoy usando el control de versiones - git, con git kraken para una interfaz gráfica. Es muy fácil de usar y ha limpiado mis archivos drásticamente, ya no es necesario que los archivos antiguos se queden pegados, o que las versiones antiguas de código hayan sido comentadas "por si acaso".
También instalé pylint y lo ejecuté en todo mi código. Un archivo obtuvo un puntaje negativo inicialmente; Ni siquiera estoy seguro de cómo fue posible. Mi archivo principal comenzó con una puntuación de ~ 1.83 / 10 y ahora está en ~ 9.1 / 10. Todo el código ahora se ajusta bastante bien a los estándares. También lo revisé con mis propios ojos actualizando nombres de variables que habían salido ... uhm ... mal, y buscando secciones para refactorizar.
En particular, hice una pregunta reciente en este sitio sobre la refactorización de una de mis funciones principales, y ahora es mucho más limpia y mucho más corta: en lugar de una función larga, hinchada, si / si no llena, ahora es menos de la mitad el tamaño y mucho más fácil descubrir qué está pasando.
Mi próximo paso es implementar "pruebas unitarias" de algún tipo. Con lo cual me refiero a un archivo que puedo ejecutar en mi archivo principal que analiza todas las funciones en él con declaraciones de aserción y try / exceptts, que probablemente no sea la mejor manera de hacerlo, y da como resultado una gran cantidad de código duplicado, pero seguiré leyendo e intentaré descubrir cómo hacerlo mejor.
También actualicé significativamente la documentación que ya había escrito y agregué archivos suplementarios como una hoja de cálculo de Excel, la documentación y un documento asociado al repositorio de github. Parece un proyecto de programación real ahora.
Entonces ... supongo que esto es todo para decir: gracias .
Respuestas:
Este es un problema bastante común para los científicos. Lo he visto mucho y siempre se debe al hecho de que la programación es algo que eliges como herramienta para hacer tu trabajo.
Entonces tus guiones son un desastre. Voy a ir en contra del sentido común y decir que, suponiendo que esté programando solo, ¡ esto no es tan malo! Nunca volverás a tocar la mayor parte de lo que escribes, así que pasar demasiado tiempo para escribir un código bonito en lugar de producir "valor" (por lo que el resultado de tu script) no te va a hacer mucho.
Sin embargo, habrá un momento en el que deberá volver a algo que hizo y ver exactamente cómo funcionaba algo. Además, si otros científicos necesitan revisar su código, es realmente importante que sea lo más claro y conciso posible, para que todos puedan entenderlo.
Su principal problema será la legibilidad, así que aquí hay algunos consejos para mejorar:
Nombres de variables:
A los científicos les encanta usar anotaciones concisas. Todas las ecuaciones matemáticas usualmente usan letras simples como variables, y no me sorprendería ver muchas y muy pequeñas variables en su código. Esto perjudica mucho la legibilidad. Cuando vuelva a su código, no recordará qué representan esos y, i y x2, y pasará mucho tiempo tratando de resolverlo. Intente nombrar sus variables explícitamente, usando nombres que representen exactamente lo que son.
Divide tu código en funciones:
Ahora que renombró todas sus variables, sus ecuaciones se ven terribles y tienen varias líneas de largo.
En lugar de dejarlo en su programa principal, mueva esa ecuación a una función diferente y asígnele un nombre. Ahora, en lugar de tener una línea de código enorme y desordenada, tendrá unas breves instrucciones que le indicarán exactamente qué está sucediendo y qué ecuación utilizó. Esto mejora tanto su programa principal, ya que ni siquiera tiene que mirar la ecuación real para saber lo que hizo, y el código de la ecuación en sí, ya que en una función separada puede nombrar sus variables como desee, y volver a Las letras individuales más familiares.
En esta línea de pensamiento, intente encontrar todas las piezas de código que representan algo, especialmente si ese algo es algo que tiene que hacer varias veces en su código, y divídalas en funciones. Descubrirá que su código será más fácil de leer rápidamente y que podrá usar las mismas funciones sin escribir más código.
En la torta, si esas funciones son necesarias en más de sus programas, simplemente puede hacer una biblioteca para ellas y las tendrá siempre disponibles.
Variables globales:
Cuando era principiante, pensaba que esta era una excelente manera de transmitir los datos que necesitaba en muchos puntos de mi programa. Resulta que hay muchas otras formas de pasar cosas, y las únicas cosas que hacen las variables globales es causar dolor de cabeza a las personas, ya que si vas a un punto aleatorio de tu programa nunca sabrás cuándo se usó o editó ese valor por última vez, rastrearlo será un dolor. Intenta evitarlos siempre que sea posible.
Si sus funciones necesitan devolver o modificar múltiples valores, haga una clase con esos valores y páselos como un parámetro, o haga que la función devuelva múltiples valores (con tuplas con nombre) y asigne esos valores en el código de la persona que llama.
Control de versiones
Esto no mejora directamente la legibilidad, pero le ayuda a hacer todo lo anterior. Cada vez que realice algunos cambios, comprométase con el control de versiones (un repositorio local de Git estará bien), y si algo no funciona, ¡mire lo que cambió o simplemente retroceda! Esto facilitará la refactorización de su código, y será una red de seguridad si accidentalmente rompe cosas.
Tener todo esto en mente le permitirá escribir código claro y más efectivo, y también lo ayudará a encontrar posibles errores más rápido, ya que no tendrá que atravesar funciones gigantescas y variables desordenadas.
fuente
Físico aquí. Estado allí.
Yo diría que su problema no se trata de la elección de herramientas o paradigmas de programación (pruebas unitarias, OOP, lo que sea). Se trata de la actitud , la mentalidad. El hecho de que sus nombres de variables estén bien elegidos al principio y terminen siendo basura es bastante revelador. Si piensa que su código es "ejecutar una vez, luego tirar", inevitablemente será un desastre. Si lo piensas como el producto de la artesanía y el amor, será hermoso.
Creo que solo hay una receta para escribir código limpio: escríbelo para el ser humano que lo va a leer, no para el intérprete que lo va a ejecutar. Al intérprete no le importa si su código es un desastre, pero al lector humano sí le importa.
Usted es un científico Probablemente puedas pasar mucho tiempo puliendo un artículo científico. Si su primer borrador parece complicado, lo refactorizará hasta que la lógica fluya de la manera más natural. Desea que sus colegas lo lean y encuentren los argumentos claros como el cristal. Desea que sus alumnos puedan aprender de él.
Escribir código limpio es exactamente lo mismo. Piense en su código como una explicación detallada de un algoritmo que solo por casualidad es legible por máquina. Imagina que vas a publicarlo como un artículo que la gente leerá. Incluso lo va a mostrar en una conferencia y guiará a la audiencia línea por línea. Ahora ensaya tu presentación . ¡Sí, línea por línea ! Vergonzoso, ¿no? Así que limpia tus diapositivas (err ... quiero decir, tu código), y ensaya nuevamente. Repita hasta que esté satisfecho con el resultado.
Sería aún mejor si, después de los ensayos, puedes mostrar tu código a personas reales en lugar de solo a personas imaginarias y a ti mismo en el futuro. Pasarlo línea por línea se llama "caminata de código", y no es una práctica tonta.
Por supuesto, todo esto tiene un costo. Escribir código limpio lleva mucho más tiempo que escribir código desechable. Solo usted puede evaluar si los beneficios superan el costo para su caso de uso particular.
En cuanto a las herramientas, he dicho antes que no son que importante. Sin embargo, si tuviera que elegir uno, diría que el control de versiones es el más útil.
fuente
El control de versiones probablemente te dará más por tu dinero. No es solo para el almacenamiento a largo plazo, es ideal para realizar un seguimiento de su experimentación a corto plazo y volver a la última versión que funcionó, manteniendo notas en el camino.
Los siguientes más útiles son las pruebas unitarias. Lo que pasa con las pruebas unitarias es que incluso las bases de código con millones de líneas de código se prueban unitariamente una función a la vez. Las pruebas unitarias se realizan en el nivel más pequeño de abstracción. Eso significa que básicamente no hay diferencia entre las pruebas unitarias escritas para bases de código pequeñas y las utilizadas para las grandes. Solo hay más de ellos.
Las pruebas unitarias son la mejor manera de evitar romper algo que ya estaba funcionando cuando arreglas algo más, o al menos decirte rápidamente cuándo lo haces. En realidad, son más útiles cuando no es tan hábil como programador, o no sabe cómo o no desea escribir un código más detallado que esté estructurado para hacer que los errores sean menos probables o más obvios.
Entre el control de versiones y las pruebas de unidades de escritura a medida que avanza, su código naturalmente se volverá mucho más limpio. Se pueden aprender otras técnicas para una codificación más limpia cuando se alcanza una meseta.
fuente
Probablemente lo habría descubierto usted mismo, pero si tiene que " grabarlo todo en un archivo de texto en alguna parte ", no está utilizando el sistema de control de versiones en todo su potencial. Use algo como Subversion, git o Mercurial y escriba un buen mensaje de confirmación con cada confirmación y tendrá un registro que cumple el propósito del archivo de texto pero que no puede separarse del repositorio.
Aparte de eso, usar el control de versiones es lo más importante que puede hacer por una razón que ninguna de las respuestas existentes menciona: la reproducibilidad de los resultados . Si puede usar sus mensajes de registro o agregar una nota a los resultados con el número de revisión, entonces puede estar seguro de poder regenerar los resultados, y estará en mejores condiciones para publicar el código con el documento.
La prueba de unidades nunca es necesaria, pero es útil si (a) el código es lo suficientemente modular como para que pueda probar unidades en lugar de todo; (b) puedes crear pruebas. Idealmente, podría escribir la salida esperada a mano en lugar de generarla con el código, aunque generarla por código al menos puede proporcionarle pruebas de regresión que le indican si algo cambió su comportamiento. Solo considere si es más probable que las pruebas tengan errores que el código que están probando.
OOP es una herramienta. Úselo si ayuda, pero no es el único paradigma. Supongo que solo conoce realmente la programación de procedimientos: si ese es el caso, entonces, en el contexto descrito, creo que se beneficiaría más de estudiar programación funcional que OOP, y en particular la disciplina de evitar los efectos secundarios cuando sea posible. Python se puede escribir en un estilo muy funcional.
fuente
En la escuela de posgrado, yo mismo escribí un código de algoritmo pesado. Es un poco duro de roer. Para decirlo en términos generales, muchas convenciones de programación se basan en la idea de poner información en una base de datos, recuperarla en el momento adecuado y luego masajear esos datos para presentarlos a un usuario, generalmente usando una biblioteca para cualquier matemática partes pesadas del algoritmo de ese proceso. Para estos programas, todo lo que ha escuchado acerca de OOP, dividir el código en funciones cortas y hacer que todo sea fácilmente comprensible de un vistazo cuando sea posible es un excelente consejo. Pero no funciona para el código de algoritmo pesado, o el código que implementa cálculos matemáticos complejos y poco más.
Si está escribiendo guiones para realizar cálculos científicos, probablemente tenga escritos con las ecuaciones o algoritmos que usa. Si está utilizando nuevas ideas que ha descubierto por su cuenta, es de esperar que las publique en sus propios documentos. En este caso, la regla es: desea que su código se lea lo más parecido posible a las ecuaciones publicadas. Aquí hay una respuesta sobre Ingeniería de Software. SE con más de 200 votos a favor que defienden este enfoque y explican cómo se ve: ¿Hay una excusa para nombres de variables cortas?
Como otro ejemplo, hay algunos fragmentos geniales de código en Simbody , una herramienta de simulación física utilizada para la investigación y la ingeniería física. Estos fragmentos tienen un comentario que muestra una ecuación que se utiliza para un cálculo, seguida de un código que se lee lo más cerca posible de las ecuaciones que se implementan.
ContactGeometry.cpp
:ContactGeometry_Sphere.cpp
:fuente
λ
o enφ
lugar de lo feolambda_
ophy
...Por lo tanto, mi trabajo diario es en la publicación y preservación de datos de investigación para el sistema de la Universidad de California. Un par de personas han mencionado la reproducibilidad, y creo que ese es realmente el problema principal aquí: documentar su código de la forma en que documentaría cualquier otra cosa que alguien necesita para reproducir su experimento e, idealmente, escribir código que lo haga sencillo para otra persona para reproducir su experimento y verificar sus resultados en busca de fuentes de error.
Pero algo que no he visto mencionado, que creo que es importante, es que las agencias de financiación están mirando cada vez más la publicación de software como parte de la publicación de datos, y haciendo que la publicación de software sea un requisito para la ciencia abierta.
Con ese fin, si desea algo específico, dirigido a investigadores en lugar de desarrolladores de software en general, no puedo recomendar la organización de Carpintería de software lo suficiente. Si puedes asistir a uno de sus talleres , genial; Si todo lo que tiene tiempo / acceso para hacer es leer algunos de sus documentos sobre las mejores prácticas de computación científica , eso también es bueno. De este último:
Un resumen de alto nivel de las prácticas que recomiendan:
El documento entra en detalles considerables sobre cada uno de estos puntos.
fuente
Respuesta personal:
también escribo muchos scripts para fines científicos. Para scripts más pequeños, simplemente trato de seguir buenas prácticas generales de programación (es decir, usar el control de versiones, practicar el autocontrol con nombres de variables). Si solo estoy escribiendo algo para abrir o visualizar rápidamente un conjunto de datos, no me molesto con OOP.
Respuesta general:
"Depende". Pero si tiene dificultades para determinar cuándo usar un concepto o paradigmas de programación, aquí hay un par de cosas en las que pensar:
# 1: Familiarícese con lo que hay ahí fuera:
aunque solo esté "escribiendo" scripts (y realmente solo le interese el componente de ciencia), debería tomarse un tiempo para aprender sobre diferentes conceptos y paradigmas de programación. De esa manera, puede tener una mejor idea de lo que debe / no debe usar y cuándo. Eso puede sonar un poco desalentador. Y aún puede tener la pregunta: "¿Dónde empiezo / qué empiezo a mirar?" Intento explicar un buen punto de partida en los próximos dos puntos.
# 2: Comience a arreglar lo que sabe que está mal:
Personalmente, comenzaría con las cosas que sé que están mal. Obtenga un poco de control de versiones y comience a disciplinarse para mejorar con esos nombres de variables (es una lucha seria). Arreglar lo que sabes que está mal puede sonar obvio. Sin embargo, en mi experiencia, descubrí que arreglar una cosa me lleva a otra, y así sucesivamente. Antes de darme cuenta, descubrí 10 cosas diferentes que estaba haciendo mal y descubrí cómo solucionarlos o cómo implementarlos de manera limpia.
# 3: Obtenga un socio de programación:
si "comenzar de nuevo" para usted no implica tomar clases formales, considere asociarse con un desarrollador y pedirles que revisen su código. Incluso si no entienden la parte científica de lo que estás haciendo, podrían decirte lo que podrías haber hecho para que tu código sea más elegante.
# 4: Busque consorcios:
no sé en qué área científica se encuentra. Pero dependiendo de lo que haga en el mundo científico, intente buscar consorcios, grupos de trabajo o participantes en la conferencia. Luego vea si hay algún estándar en el que estén trabajando. Eso puede llevarlo a algunos estándares de codificación. Por ejemplo, hago mucho trabajo geoespacial. Mirar los documentos de la conferencia y los grupos de trabajo me llevaron al Consorcio Geoespacial Abierto . Una de las cosas que hacen es trabajar en estándares para el desarrollo geoespacial.
¡Espero que eso ayude!
Nota al margen: Sé que acabas de usar OOP como ejemplo. No quería que pensaras que me quedé atascado en cómo manejar la escritura de código usando OOP. Era más fácil escribir una respuesta continuando con ese ejemplo.
fuente
Recomiendo seguir el principio de Unix: ¡ Mantenlo simple, estúpido! (BESO)
O, dicho de otra manera: haz una cosa a la vez y hazlo bien.
Qué significa eso? Bueno, antes que nada, significa que tus funciones deberían ser cortas. Cualquier función que no pueda entenderse completamente en propósito, uso e implementación dentro de unos segundos es definitivamente demasiado larga. Es probable que haga varias cosas a la vez, cada una de las cuales debería ser una función propia. Así que divídelo.
En términos de líneas de código, mi heurística es que 10 líneas son una buena función, y cualquier cosa más allá de 20 es muy probable. Otras personas tienen otras heurísticas. La parte importante es mantener la longitud a algo que realmente pueda comprender en un instante.
¿Cómo se divide una función larga? Bueno, primero buscas patrones repetidos de código. Luego factoriza estos patrones de código, les da un nombre descriptivo y observa cómo se reduce su código . Realmente, la mejor refactorización es la refactorización que reduce el tamaño del código.
Esto es especialmente cierto cuando la función en cuestión se ha programado con copiar y pegar. Cada vez que veas un patrón tan repetido, sabrás instantáneamente que esto probablemente debería convertirse en una función propia. Este es el principio de No te repitas (DRY) . Cada vez que presionas copiar y pegar, estás haciendo algo mal. Crea una función en su lugar.
Algunas funciones pueden ser largas porque están haciendo varias cosas distintas una tras otra. Estas no son violaciones SECAS, pero también se pueden dividir. El resultado es con frecuencia una función de alto nivel que llama a un puñado de funciones que implementan los pasos individuales de las funciones originales. Esto generalmente aumentará el tamaño del código, pero los nombres de las funciones agregadas hacen maravillas para hacer que el código sea más legible. Porque ahora tiene una función de nivel superior con todos sus pasos nombrados explícitamente. Además, después de esta división, está claro qué paso opera en qué datos. (Argumentos de función. No utiliza variables globales, ¿verdad?)
Una buena heurística para este tipo de división de funciones seccionales es cuando se siente tentado a escribir un comentario de sección, o cuando encuentra un comentario de sección en su código. Es muy probable que este sea uno de los puntos donde su función debe dividirse. El comentario de la sección también puede servir para inspirar un nombre para la nueva función.
Los principios KISS y DRY pueden llevarte lejos. No necesita comenzar con OOP, etc. inmediatamente, a menudo puede lograr grandes simplificaciones simplemente aplicando estos dos. Sin embargo, a la larga vale la pena saber acerca de OOP y otros paradigmas porque le brindan herramientas adicionales que puede usar para aclarar el código de su programa.
Finalmente, registra cada acción con un commit. Factorizas algo en una nueva función, eso es un commit . Fusionas dos funciones en una, porque realmente hacen lo mismo, eso es un commit . Si cambia el nombre de una variable, es una confirmación . Comprometerse con frecuencia. Si pasa un día y no te comprometiste, es probable que hayas hecho algo mal.
fuente
Estoy de acuerdo con los demás en que el control de versiones resolverá muchos de sus problemas de inmediato. Específicamente:
Yo diría que no lo pienses demasiado: solo usa git. Apéguese a comandos simples (por ejemplo, solo una
master
rama), quizás use una GUI, y debería estar bien. Como beneficio adicional, puede usar gitlab, github, etc. para publicaciones y copias de seguridad gratuitas;)La razón por la que escribí esta respuesta fue para abordar dos cosas que podrías probar que no he visto mencionadas anteriormente. El primero es usar aserciones como una alternativa ligera a las pruebas unitarias. Las pruebas unitarias tienden a ubicarse "fuera" de la función / módulo / lo que sea que se esté probando: generalmente envían algunos datos a una función, reciben un resultado y luego verifican algunas propiedades de ese resultado. Generalmente, esta es una buena idea, pero puede ser inconveniente (especialmente para el código "desechable") por algunas razones:
Las afirmaciones no tienen estos inconvenientes, ya que se verifican durante la ejecución normal de un programa. En particular:
Usted menciona la velocidad como un factor, en cuyo caso la comprobación de aserciones puede ser indeseable en ese ciclo (pero aún así es útil para verificar la configuración y el procesamiento posterior). Sin embargo, casi todas las implementaciones de afirmaciones proporcionan una forma de desactivarlas; por ejemplo, en Python , aparentemente se pueden deshabilitar ejecutando la
-O
opción (no lo sabía, ya que nunca antes había sentido la necesidad de deshabilitar ninguna de mis afirmaciones). Yo recomendaría que los dejes enpor defecto; Si su ciclo de codificación / depuración / prueba se ralentiza, es mejor que pruebe con un subconjunto más pequeño de sus datos, o realice menos iteraciones de alguna simulación durante la prueba, o lo que sea. Si termina deshabilitando aserciones en ejecuciones que no son de prueba por razones de rendimiento, lo primero que le recomiendo que haga es medir si en realidad son la fuente de la desaceleración. (Es muy fácil engañarse a nosotros mismos cuando se trata de cuellos de botella de rendimiento)Mi último consejo sería utilizar un sistema de compilación que gestione sus dependencias. Personalmente uso Nix para esto, pero también he escuchado cosas buenas sobre Guix . También hay alternativas como Docker, que son mucho menos útiles desde una perspectiva científica pero quizás un poco más familiares.
Los sistemas como Nix se están volviendo recientemente (un poco) populares, y algunos podrían considerarlos excesivos para el código "desechable" como usted describe, pero su beneficio para la reproducibilidad de la computación científica es enorme. Considere un script de shell para ejecutar un experimento, como este (por ejemplo
run.sh
):En cambio, podemos reescribirlo en una "derivación" de Nix, como esta (por ejemplo
run.nix
):El elemento intermedio
''...''
es el código bash, el mismo que teníamos antes, excepto que${...}
puede usarse para "empalmar" en el contenido de otras cadenas (en este caso./.
, que se expandirá a la ruta del directorio que contienerun.nix
). Lawith import ...
línea importa la biblioteca estándar de Nix , que permiterunCommand
ejecutar código bash. Podemos ejecutar nuestro experimento usandonix-build run.nix
, lo que dará un camino como/nix/store/1wv437qdjg6j171gjanj5fvg5kxc828p-output.csv
.Entonces, ¿qué nos compra esto? Nix configurará automáticamente un entorno "limpio", que solo tiene acceso a las cosas que pedimos explícitamente. En particular, no tiene acceso a variables como
$HOME
o ninguno de los software del sistema que hemos instalado. Esto hace que el resultado sea independiente de los detalles de nuestra máquina actual, como el contenido~/.config
o las versiones de los programas que tenemos instalados; ¡También conocido como el material que impide que otras personas reproduzcan nuestros resultados! Por eso agregué quecp
comando, ya que el proyecto no será accesible por defecto. Puede parecer molesto que el software del sistema no esté disponible para un script Nix, pero también es al revés: no necesitamos nada instalado en nuestro sistema (que no sea Nix) para usarlo en un script; solo lo pedimos y Nix se apagará y buscará / compilará / lo que sea necesario (la mayoría de las cosas se descargarán como archivos binarios; ¡la biblioteca estándar también es enorme!). Por ejemplo, si queremos un montón de paquetes particulares de Python y Haskell, para algunas versiones particulares de esos idiomas, además de algunos otros archivos basura (porque ¿por qué no?):Lo mismo
nix-build run.nix
ejecutará esto, recuperando todo lo que pedimos primero (y almacenando todo en caché en caso de que lo deseemos más adelante). La salida (cualquier archivo / directorio llamado$out
) será almacenada por Nix, que es la ruta que escupe. Se identifica por el hash criptográfico de todas las entradas que solicitamos (contenido del script, otros paquetes, nombres, indicadores del compilador, etc.); esos otros paquetes se identifican mediante hashes de sus entradas, y así sucesivamente, de modo que tengamos una cadena completa de provincias para todo, hasta la versión de GCC que compiló la versión de GCC que compiló bash, ¡y así sucesivamente!Con suerte, he demostrado que esto nos compra mucho para el código científico, y es bastante fácil comenzar con él. Los científicos también están empezando a tomarlo muy en serio, por ejemplo, (top hit de Google) https://dl.acm.org/citation.cfm?id=2830172, por lo que podría ser una habilidad valiosa para cultivar (al igual que la programación)
fuente
Sin ir al tipo de mentalidad de control de versiones completo + embalaje + pruebas unitarias (que son buenas prácticas de programación que debería intentar lograr en algún momento), una solución intermedia que creo que encajaría es usar Jupiter Notebook . Esto parece integrarse mejor con la computación científica.
Tiene la ventaja de que puedes mezclar tus pensamientos con el código; explicando por qué un enfoque es mejor que otro y dejando el código antiguo como está en una sección ad-hoc. Además de usar las celdas correctamente, naturalmente lo llevará a fragmentar su código y organizarlo en funciones que pueden ayudarlo a comprenderlo.
fuente
Las principales respuestas ya son buenas, pero quería abordar algunas de sus preguntas directamente.
El tamaño del código no está directamente relacionado con la necesidad de pruebas unitarias. Está relacionado indirectamente: las pruebas unitarias son más valiosas en bases de código complejas , y las bases de código pequeñas generalmente no son tan complejas como las más grandes.
Las pruebas unitarias brillan para el código donde es fácil cometer errores, o cuando va a tener muchas implementaciones de este código. Las pruebas unitarias hacen poco para ayudarlo con el desarrollo actual , pero hacen mucho para evitar que cometa errores en el futuro que provoquen que el código existente se comporte de repente (aunque no haya tocado esa cosa).
Supongamos que tiene una aplicación donde la Biblioteca A realiza la cuadratura de los números, y la Biblioteca B aplica el teorema de Pitágoras. Obviamente, B depende de A. Necesitas arreglar algo en la biblioteca A, y digamos que introduces un error que cubica los números en lugar de cuadrarlos.
La Biblioteca B comenzará a comportarse mal de repente, posiblemente arrojando excepciones o simplemente dando un resultado incorrecto. Y cuando mira el historial de versiones de la biblioteca B, ve que no ha sido tocado. El resultado final problemático es que no tiene indicios de lo que podría estar yendo mal, y tendrá que depurar el comportamiento de B antes de darse cuenta de que el problema está en A. Eso es un esfuerzo perdido.
Ingrese las pruebas unitarias. Estas pruebas confirman que la biblioteca A está funcionando según lo previsto. Si introduce un error en la biblioteca A que hace que arroje malos resultados, sus pruebas unitarias lo detectarán. Por lo tanto, no se quedará atrapado tratando de depurar la biblioteca B.
Esto está más allá de su alcance, pero en un desarrollo de integración continua, las pruebas unitarias se ejecutan cada vez que alguien comete algún código, lo que significa que sabrá que rompió algo lo antes posible.
Especialmente para operaciones matemáticas complicadas, las pruebas unitarias pueden ser una bendición. Realiza algunos cálculos de ejemplo y luego escribe pruebas unitarias que comparan su salida calculada y su salida real (en función de los mismos parámetros de entrada).
Sin embargo, tenga en cuenta que las pruebas unitarias no lo ayudarán a crear un buen código, sino que lo mantendrán . Si generalmente escribe código una vez y nunca lo vuelve a visitar, las pruebas unitarias serán menos beneficiosas.
OOP es una forma de pensar sobre entidades distintas, por ejemplo:
Compare esto con cómo un programador funcional piensa acerca de las cosas:
Manzanas y naranjas. Ninguno de los dos es objetivamente mejor que el otro. Una cosa interesante a tener en cuenta es que para OOP,
Vendor
se menciona dos veces, pero se refiere a lo mismo. Sin embargo, para la programación funcional,talktoVendor()
ypayVendor()
son dos cosas separadas.Esto muestra la diferencia entre los enfoques. Si hay mucha lógica compartida específica del proveedor entre estas dos acciones, OOP ayuda a reducir la duplicación de código. Sin embargo, si no hay una lógica compartida entre los dos, fusionarlos en un solo
Vendor
es un trabajo inútil (y, por lo tanto, la programación funcional es más eficiente).La mayoría de las veces, los cálculos matemáticos y científicos son operaciones distintas que no se basan en la lógica / fórmulas compartidas implícitas. Debido a eso, la programación funcional se usa con más frecuencia que OOP.
Su pregunta implica que la definición de "código bueno y limpio" cambia si está haciendo programación científica o trabajando en proyectos más grandes (supongo que se refiere a la empresa).
La definición de buen código no cambia. La necesidad de evitar la complejidad (que se puede hacer escribiendo un código limpio), sin embargo, no cambia.
El mismo argumento vuelve aquí.
Obtengo la distinción que está haciendo aquí, pero cuando mira hacia atrás al código existente, está viendo tanto las matemáticas como la programación. Si cualquiera es artificial o complejo, entonces tendrá dificultades para leerlo.
Dejando a un lado los principios de OOP, la razón principal por la que escribo clases para albergar algunos valores de datos es porque simplifica la declaración de los parámetros del método y los valores de retorno. Por ejemplo, si tengo muchos métodos que usan una ubicación (par lat / lon), me cansaré rápidamente de tener que escribir
float latitude, float longitude
y preferiré escribirLocation loc
.Esto se complica aún más cuando considera que los métodos generalmente devuelven un valor (a menos que existan características específicas del idioma para devolver más valores), y cosas como una ubicación querrían que devuelva dos valores (lat + lon). Esto lo incentiva a crear una
Location
clase para simplificar su código.Otra cosa interesante a tener en cuenta es que puede usar OOP sin mezclar valores de datos y métodos. No todos los desarrolladores están de acuerdo aquí (algunos lo llaman antipatrón), pero puede tener modelos de datos anémicos en los que tiene clases de datos separadas (campos de valores de almacenamiento) y clases lógicas (métodos de almacenamiento).
Esto es, por supuesto, en un espectro. No necesita estar completamente anémico, puede usarlo cuando lo considere apropiado.
Por ejemplo, un método que simplemente concatena el nombre y el apellido de una persona todavía se puede alojar en la
Person
clase en sí, porque no es realmente "lógica" sino más bien un valor calculado.Una clase siempre es tan grande como la suma de sus campos. Tomando el ejemplo de
Location
nuevo, que consta de dosfloat
valores, es importante notar aquí que un soloLocation
objeto ocupará tanta memoria como dosfloat
valores separados .En ese sentido, no importa si estás usando OOP o no. La huella de memoria es la misma.
El rendimiento en sí tampoco es un gran obstáculo para cruzar. La diferencia entre, por ejemplo, usar un método global o un método de clase no tiene nada que ver con el rendimiento en tiempo de ejecución, pero tiene mucho que ver con la generación de código de bytes en tiempo de compilación.
Piénselo de esta manera: si escribo mi receta de pastel en inglés o español, no cambia el hecho de que el pastel tardará 30 minutos en hornearse (= rendimiento en tiempo de ejecución). Lo único que cambia el idioma de la receta es cómo el cocinero mezcla los ingredientes (= compilando el código de bytes).
Para Python específicamente, no necesita precompilar explícitamente el código antes de llamarlo. Sin embargo, cuando no precompila, la compilación se producirá al intentar ejecutar el código. Cuando digo "tiempo de ejecución", me refiero a la ejecución en sí, no a la compilación que podría preceder a la ejecución.
fuente
Beneficios del código científico limpio
Puede ser útil considerar su código desde la perspectiva de un futuro codificador.
Por mi experiencia,
El código limpio debería facilitar la verificación de los resultados.
Es posible que desee dividir su programa para que los algoritmos individuales se puedan comparar por separado.
Evite escribir funciones con efectos secundarios contraintuitivos donde una operación no relacionada hace que otra operación se comporte de manera diferente. Si no puede evitarlo, documente qué necesita su código y cómo configurarlo.
El código limpio puede servir como código de ejemplo para futuros codificadores
Los comentarios claros (incluidos los que muestran cómo se deben llamar las funciones) y las funciones bien separadas pueden marcar una gran diferencia en el tiempo que le toma a alguien que recién comienza (o en el futuro) hacer algo útil de su trabajo.
Además de esto, crear una "API" real para su algoritmo puede prepararlo mejor si decide convertir sus scripts en una biblioteca real para que otra persona la use.
Recomendaciones
"Citar" fórmulas matemáticas usando comentarios.
John Smith Method from Some Book 1st Ed. Section 1.2.3 Pg 180
, si encontró la fórmula en un sitio web o en un documento, cite eso también.Usa los comentarios sabiamente
Si puede mejorar la legibilidad de su código utilizando buenos nombres de variables / nombres de funciones, hágalo primero. Recuerda que los comentarios se mantendrán para siempre hasta que los elimines, así que intenta hacer comentarios que no pasen de moda.
Usar nombres de variables descriptivas
xBar_AverageVelocity
Escriba código para ejecutar su programa contra datos conocidos buenos y malos conocidos.
Creo que las pruebas unitarias pueden ser útiles, creo que la mejor forma de pruebas unitarias para el código científico es una serie de pruebas que se ejecutan en datos conocidos buenos y malos.
Escriba un código para ejecutar su algoritmo y verifique hasta qué punto el resultado se desvía de lo que espera. Esto lo ayudará a encontrar problemas (potencialmente muy malos y difíciles de encontrar) en los que codifica accidentalmente algo que causa un resultado falso positivo o comete un error que hace que la función siempre devuelva el mismo valor.
Tenga en cuenta que esto se puede hacer en cualquier nivel de abstracción. Por ejemplo, podría probar un algoritmo completo de coincidencia de patrones, o puede probar una función que simplemente calcule la distancia entre dos resultados en su proceso de optimización. Comience con las áreas que son más cruciales para sus resultados primero, y / o las partes del código que le preocupan más.
Facilite agregar nuevos casos de prueba, considere agregar funciones "auxiliares" y estructurar sus datos de entrada de manera efectiva. Esto puede significar posiblemente guardar datos de entrada en el archivo para que pueda volver a ejecutar fácilmente las pruebas, aunque tenga mucho cuidado para evitar falsos positivos o casos de prueba sesgados / resueltos trivialmente.
Considere usar algo como validación cruzada , consulte esta publicación sobre validación cruzada para obtener más información.
Usar control de versiones
Recomendaría usar el control de versiones y alojar su repositorio en un sitio externo. Hay sitios que alojarán repositorios de forma gratuita.
Ventajas:
Tenga precaución al copiar / pegar código
Copiar / pegar código puede ahorrarle tiempo, pero es una de las cosas más peligrosas que puede hacer, especialmente si es código que no escribió usted mismo (por ejemplo, si es código de un colega).
Tan pronto como haga que el código funcione y lo pruebe, le recomiendo que lo revise con mucho cuidado para cambiar el nombre de cualquier variable o comentar cualquier cosa que no entienda.
fuente
Las herramientas del comercio generalmente se inventan para resolver una necesidad. Si tiene la necesidad de usar la herramienta, si no, lo más probable es que no tenga que hacerlo.
Específicamente, los programas científicos no son el objetivo final, son los medios. Usted escribe el programa para resolver un problema que tiene ahora; no espera que otros lo utilicen (y tengan que mantenerlo) en diez años. Eso solo significa que no tiene que pensar en ninguna de las herramientas que permiten al desarrollador actual registrar el historial de otros, como el control de versiones, o capturar la funcionalidad en código como pruebas unitarias.
¿Qué te beneficiaría entonces?
fuente
Además de los buenos consejos que ya están aquí, es posible que desee considerar el propósito de su programación y, por lo tanto, lo que es importante para usted.
"Se trata más de matemáticas o ciencias que estoy probando o investigando con la programación".
Si el propósito es experimentar y probar algo para su propia comprensión y sabe cuáles deberían ser los resultados, entonces su código es básicamente un descarte rápido y su enfoque actual puede ser suficiente, aunque podría mejorarse. Si los resultados no son los esperados, puede volver y revisar.
Sin embargo, si los resultados de su codificación informan la dirección de su investigación y no sabe cuáles deberían ser los resultados , entonces la corrección se vuelve particularmente importante. Un error en su código podría llevarlo a sacar conclusiones incorrectas de su experimento con una variedad de malas implicaciones para su investigación general.
En ese caso, dividir su código en funciones fácilmente comprensibles y verificables con pruebas unitarias le proporcionará ladrillos de construcción más sólidos, lo que le dará más confianza en sus resultados y puede evitarle mucha frustración más adelante.
fuente
Tan bueno como el control de versiones y las pruebas unitarias son para mantener su código general organizado y funcional, ninguno de los dos le ayuda a escribir un código más limpio.
Si desea evitar escribir código desordenado, necesita una herramienta que funcione donde ocurre el desorden: cuando está escribiendo el código. Un tipo popular de herramienta que se llama linter. No soy un desarrollador de Python, pero parece que Pylint podría ser una buena opción.
Un linter analiza el código que ha escrito y lo compara con un conjunto configurable de mejores prácticas. Si el linter tiene una regla que deben ser las variables
camelCase
, y usted escribe unasnake_case
, marcará eso como un error. Los buenos linters tienen reglas que van desde "se deben usar variables declaradas" hasta "La complejidad ciclomática de las funciones debe ser menor que 3".La mayoría de los editores de código se pueden configurar para ejecutar un linter cada vez que guarde, o solo generalmente mientras escribe, e indique problemas en línea. Si escribe algo como
x = 7
,x
se resaltará, con una instrucción para usar un nombre mejor y más largo (si eso es lo que ha configurado). Esto funciona como un corrector ortográfico en la mayoría de los procesadores de texto, lo que hace que sea difícil de ignorar y ayuda a desarrollar mejores hábitos.fuente
Todo lo que enumeró es una herramienta en la caja de herramientas metafórica. Como cualquier cosa en la vida, diferentes herramientas son apropiadas para diferentes tareas.
En comparación con otros campos de ingeniería, el software funciona con un montón de piezas individuales que, por sí mismas, son bastante simples. Una declaración de asignación no se evalúa de manera diferente dependiendo de las fluctuaciones de temperatura de la habitación. Una
if
declaración no se corroe en su lugar y sigue devolviendo lo mismo después de un tiempo. Pero debido a que los elementos individuales son tan simples y el software creado por humanos, esos elementos se combinan en piezas cada vez más grandes hasta que el resultado se vuelve tan grande y complejo que alcanza los límites de lo que las personas pueden manejar mentalmente.A medida que los proyectos de software crecieron y crecieron, las personas los estudiaron y crearon herramientas para tratar de manejar esa complejidad. OOP es un ejemplo. Más y más lenguajes de programación abstractos son otro medio. Debido a que gran parte del dinero en software está haciendo más y más , las herramientas para lograr eso son lo que va a ver y leer. Pero parece que esas situaciones no se aplican a usted.
Por lo tanto, no sienta que necesita hacer nada de eso. Al final del día, el código es solo un medio para un fin. Desafortunadamente, lo que mejor le dará la perspectiva correcta sobre lo que es y lo que no es apropiado es trabajar en algunos proyectos más grandes, ya que es mucho más difícil saber lo que falta cuando la caja de herramientas es su mente.
En cualquier caso, no me preocuparía por no usar OOP u otras técnicas siempre que sus scripts sean pequeños. Muchos de los problemas que describió son solo habilidades organizativas profesionales generales, es decir, no perder un archivo antiguo es algo con lo que todos los campos tienen que lidiar.
fuente
Además de todas las buenas sugerencias proporcionadas hasta ahora, una práctica que aprendí con el tiempo y que considero esencial es agregar de manera muy liberal comentarios detallados a su código. Es lo más importante para mí cuando vuelvo a algo después de un largo lapso de tiempo. Explícate lo que estás pensando. Lleva un poco de tiempo hacerlo, pero es relativamente fácil y casi sin dolor.
A veces tengo dos o tres veces más líneas de comentarios que del código, especialmente cuando los conceptos o técnicas son nuevos para mí y me explican.
Controle la versión, mejore sus prácticas, etc. ... todo lo anterior. Pero explíquese las cosas a medida que avanza. Funciona muy bien
fuente
¿Qué cualidades son importantes para este tipo de programa?
Probablemente no importa si es fácil de mantener o evolucionar, porque lo más probable es que eso no suceda.
Probablemente no importa cuán eficiente sea.
Probablemente no importa si tiene una excelente interfaz de usuario o si es segura contra atacantes maliciosos.
Puede importar que sea legible: que alguien que lea su código pueda convencerse fácilmente de que hace lo que dice hacer.
Ciertamente importa que sea correcto. Si el programa da resultados incorrectos, esas son sus conclusiones científicas fuera de la ventana. Pero solo necesita procesar correctamente la entrada que realmente le está pidiendo que procese; realmente no importa mucho si se cae si se le dan valores de datos de entrada negativos, si todos sus valores de datos son positivos.
También importa que mantenga cierto nivel de control de cambios. Sus resultados científicos deben ser reproducibles, y eso significa que necesita saber qué versión del programa produjo los resultados que pretende publicar. Debido a que solo hay un desarrollador, el control de cambios no necesita ser muy elaborado, pero debe asegurarse de que puede regresar a un punto en el tiempo y reproducir sus resultados.
Así que no se preocupe por los paradigmas de programación, la orientación a objetos, la elegancia algorítmica. Preocúpese por la claridad y la legibilidad y por la trazabilidad de sus cambios a lo largo del tiempo. No te preocupes por la interfaz de usuario. No se preocupe por probar todas las combinaciones posibles de parámetros de entrada, pero haga suficientes pruebas para estar seguro (y para que otros confíen) en que sus resultados y conclusiones son válidos.
fuente
He trabajado en un entorno similar con académicos que escriben mucho código (matemático / científico) pero su progreso es lento debido a las mismas razones que usted describe. Sin embargo, he notado una cosa particular que salió bien que creo que también puede ayudarlo: crear y mantener una colección de bibliotecas especializadas que se pueden usar en múltiples proyectos. Estas bibliotecas deberían proporcionar funciones de utilidad y, por lo tanto, ayudarán a mantener su proyecto actual específico para el dominio del problema.
Por ejemplo, puede que tenga que lidiar con muchas transformaciones de coordenadas en su campo (ECEF, NED, lat / lon, WGS84, etc.), por lo que una función como
convert_ecef_to_ned()
debería entrar en un nuevo proyecto llamadoCoordinateTransformations
. Ponga el proyecto bajo control de versiones y hágalo en los servidores de su departamento para que otras personas puedan usarlo (y con suerte mejorarlo). Luego, después de unos años, debe tener una colección robusta de bibliotecas con sus proyectos que solo contengan código específico para un problema particular / dominio de investigación.Algunos consejos más generales:
fuente
Las siguientes son mis opiniones y están muy influenciadas por mi propio camino particular.
La codificación a menudo genera perspectivas dogmáticas sobre cómo debe hacer las cosas. En lugar de técnicas y herramientas, creo que debe analizar los valores y costos acumulativos para decidir una estrategia adecuada.
Escribir un código sólido, legible, depurable y sólido requiere mucho tiempo y esfuerzo. En muchos casos, dado un horizonte de planificación limitado, no vale la pena hacerlo (parálisis de análisis).
Un colega tenía una regla general; si está haciendo esencialmente el mismo tipo de cosas por tercera vez, invierta el esfuerzo, de lo contrario, un trabajo rápido y sucio es apropiado.
Las pruebas de algún tipo son esenciales, pero para proyectos únicos, simplemente la observación puede ser suficiente. Para cualquier cosa sustancial, las pruebas y la infraestructura de prueba son esenciales. El valor es que lo libera al codificar, el costo es que si la prueba se enfoca en una implementación particular, entonces las pruebas también necesitan mantenimiento. Las pruebas también le recuerdan cómo se supone que funcionan las cosas.
Para mis propios guiones únicos (a menudo para cosas como validar una estimación de probabilidad o similar), encontré dos cosas pequeñas muy útiles: 1. Incluya un comentario que muestre cómo se usa el código. 2. Incluya una breve descripción de por qué escribió el código. Estas cosas son terriblemente obvias cuando escribes el código, pero la obviedad desperdicia tiempo :-).
OOP se trata de reutilizar código, abstraer, encapsular, factorizar, etc. Muy útil, pero fácil de perder si la producción de código y diseño de calidad no es su objetivo final. Se necesita tiempo y esfuerzo para producir cosas de calidad.
fuente
Si bien creo que las pruebas unitarias tienen sus méritos, tienen un valor dudoso para el desarrollo científico: a menudo son demasiado pequeñas para ofrecer mucho valor.
Pero realmente me gustan las pruebas de integración para el código científico:
Aísle una pequeña porción de su código que podría funcionar por sí mismo, por ejemplo, la tubería ETL. Luego escriba una prueba que proporcione los datos, ejecute la tubería etl (o solo un paso) y luego pruebe que el resultado coincida con sus expectativas. Si bien el fragmento probado puede ser un montón de código, la prueba proporciona aún valor:
Estoy usando esta técnica a menudo, y a menudo termino con una función principal relativamente legible, pero las subfunciones suelen ser bastante largas y feas, pero pueden modificarse y reorganizarse rápidamente debido a los límites de E / S robustos.
fuente
Normalmente trabajo en una base de fuente muy grande. Usamos todas las herramientas que mencionas. Recientemente, comencé a trabajar en algunos scripts de Python para un proyecto paralelo. Son unas pocas docenas a unos cientos de líneas como máximo. Por costumbre, confié mis guiones al control de fuente. Esto ha sido útil porque puedo crear ramas para probar experimentos que podrían no funcionar. Puedo bifurcar si necesito duplicar el código y modificarlo para otro propósito. Esto deja el original intacto en caso de que necesite volver a sacarlo.
Para las "pruebas unitarias" solo tengo algunos archivos de entrada que están destinados a producir una salida conocida que verifico a mano. Probablemente podría automatizarlo, pero parece que tomaría más tiempo hacerlo de lo que ahorraría al hacerlo. Probablemente depende de con qué frecuencia tengo que modificar y ejecutar los scripts. De cualquier manera, si funciona, hazlo. Si es más problema de lo que vale, no pierdas tu tiempo.
fuente
Con la escritura de código, como con la escritura en general, la pregunta principal es:
Cosas como las pautas formales de codificación no tienen sentido cuando eres tu único público.
Dicho esto, por otro lado, sería útil escribir el código de una manera, su futuro es capaz de entenderlo de inmediato.
Por lo tanto, un "buen estilo" sería el que más le ayuda. El aspecto que debería tener ese estilo es una respuesta que no puedo dar.
Creo que no necesita OOP o pruebas unitarias para archivos de 150 LOC. Un VCS dedicado sería interesante cuando tienes un código en evolución. De lo contrario, a
.bak
hace el truco. Estas herramientas son una cura para una enfermedad, es posible que ni siquiera tenga.Tal vez debería escribir su código de tal manera que, incluso si lo lee mientras está borracho, puede leerlo, comprenderlo y modificarlo.
fuente