¿Ser estúpido para obtener una mejor productividad?

110

He pasado mucho tiempo leyendo diferentes libros sobre "buen diseño", "patrones de diseño", etc. Soy un gran fan de SOLID y cada vez que necesito escribir un simple fragmento de código, pienso en el futuro. Por lo tanto, si la implementación de una nueva característica o una corrección de errores requiere solo agregar tres líneas de código como esta:

if(xxx) {
    doSomething();
}

No significa que lo haré de esta manera. Si siento que es probable que esta pieza de código se haga más grande en el futuro más cercano, pensaré en agregar abstracciones, mover esta funcionalidad a otro lugar y así sucesivamente. El objetivo que persigo es mantener la complejidad promedio igual a la que tenía antes de mis cambios.

Creo que, desde el punto de vista del código, es una buena idea; mi código nunca es lo suficientemente largo y es bastante fácil de entender los significados de diferentes entidades, como clases, métodos y relaciones entre clases y objetos.

El problema es que toma mucho tiempo y, a menudo, creo que sería mejor si solo implementara esa característica "tal como está". Se trata de "tres líneas de código" frente a "nueva interfaz + dos clases para implementar esa interfaz".

Desde el punto de vista de un producto (cuando hablamos del resultado ), las cosas que hago no tienen ningún sentido. Sé que si vamos a trabajar en la próxima versión, tener un buen código es realmente genial. Pero por otro lado, el tiempo que ha dedicado a hacer que su código sea "bueno" puede haberse empleado en la implementación de algunas características útiles.

A menudo me siento muy insatisfecho con mis resultados: un código bueno que solo puede hacer A es peor que un código malo que puede hacer A, B, C y D.

¿Es probable que este enfoque resulte en una ganancia neta positiva para un proyecto de software, o es una pérdida de tiempo?

    
pregunta loki2302 28.07.2017 - 15:36
fuente

17 respuestas

152
  

el código bueno que solo puede hacer A es peor que el código malo que puede hacer A, B, C, D.

Esto me huele a generalidad especulativa . Sin saber (o al menos estar razonablemente seguro) de que sus clientes van a necesitar las funciones B, C y D, simplemente está complicando excesivamente su diseño. Un código más complejo es más difícil de entender y mantener a largo plazo. La complejidad adicional se justifica solo por las características adicionales útiles . Pero normalmente somos muy malos para predecir el futuro. La mayoría de las funciones que creemos que pueden ser necesarias en el futuro nunca se solicitarán en la vida real.

Entonces:

El código bueno que solo puede hacer A (pero está haciendo eso de manera simple y limpia) es MEJOR que el código malo que puede hacer A, B, C, D (algunos de los cuales podrían se necesitará en algún momento en el futuro).

    
respondido por el Péter Török 06.09.2011 - 13:22
fuente
87

Tiempo de anécdota:

He tenido dos desarrolladores trabajando para mí que se inclinaron hacia la ingeniería excesiva de esta manera.

Para uno de ellos, esto básicamente detuvo su productividad, especialmente al iniciar un nuevo proyecto. Más especialmente si el proyecto era, por su naturaleza, bastante simple. En última instancia, una pieza de software que funciona ahora es lo que necesitamos. Esto se puso tan mal que tuve que dejarlo ir.

El otro desarrollador que era propenso a la ingeniería excesiva lo compensó por ser extremadamente productivo. Como tal, a pesar de todo el código extraño, él todavía entregó más rápido que la mayoría. Sin embargo, ahora que ha avanzado, a menudo me siento irritado por la cantidad de trabajo adicional que se necesita para agregar funcionalidad, ya que tiene que modificar capas de abstracción (totalmente innecesarias), etc.

Así que la moraleja es que el exceso de ingeniería consumirá tiempo adicional que se podría invertir mejor en hacer cosas útiles. Y no solo su propio tiempo, sino también de aquellos que tienen que trabajar con su código.

Así que no.

Desea que su código sea lo más simple posible (y no más sencillo). Manejar 'maybes' no hace que las cosas sean más simples, si adivinas mal, habrás hecho el código más complejo sin ninguna ganancia real para mostrarlo.

    
respondido por el Kris 06.09.2011 - 12:55
fuente
26

Los principios SOLID y KISS / YAGNI son opuestos casi polares. Uno le dirá que si doSomething () no se puede justificar como parte integral del trabajo que está realizando la clase que lo llama, debe estar en una clase diferente que esté acoplada e inyectada de manera flexible. El otro le dirá que si este es el lugar en el que se utiliza DoSomething, incluso extraerlo en un método puede haber sido excesivo.

Esto es lo que hace que los buenos programadores valgan su peso en oro. La estructura "adecuada" es una base de caso por caso, que requiere el conocimiento del código base actual, la ruta futura del programa y las necesidades de la empresa detrás del programa.

Me gusta seguir esta sencilla metodología de tres pasos.

  • En el primer paso, haz que funcione.
  • En el segundo paso, hazlo limpio.
  • En el tercer paso, hazlo SÓLIDO.

Básicamente, así es como se mezcla KISS con SOLID. Cuando escribes una línea de código por primera vez, por lo que sabes, será de una sola vez; simplemente tiene que funcionar y a nadie le importa cómo, así que no te hagas ilusiones. La segunda vez que coloca el cursor en esa línea de código, ha refutado su hipótesis original; al volver a visitar este código, es probable que lo estés extendiendo o conectando desde otro lugar. Ahora, deberías limpiarlo un poco; extraiga métodos para los bits de información más utilizados, reduzca o elimine la codificación de copiar / pegar, agregue algunos comentarios, etc. La tercera vez que vuelva a este código, ahora es una intersección importante para las rutas de ejecución de su programa. Ahora debe tratarlo como una parte central de su programa y aplicar las reglas de SOLID cuando sea posible.

Ejemplo: necesitas escribir una simple línea de texto en la consola. La primera vez que esto sucede, Console.WriteLine () está bien. Luego regresa a este código después de que los nuevos requisitos también dictan que se escriba la misma línea en un registro de salida. En este simple ejemplo, puede que no haya un montón de código repetitivo de "copiar / pegar" (o quizás haya), pero aún puede hacer una limpieza básica, tal vez extraer un método o dos para evitar que las operaciones de IO se integren en una lógica de negocios más profunda . Luego, regresa cuando el cliente desea la misma salida de texto a una canalización con nombre para un servidor de monitoreo. Ahora, esta rutina de salida es un gran problema; Estás transmitiendo el mismo texto de tres maneras. Este es el ejemplo de libro de texto de un algoritmo que se beneficiaría de un patrón compuesto; desarrolle una interfaz IWriter simple con un método Write (), implemente esa interfaz para crear las clases ConsoleWriter, FileWriter y NamedPipeWriter, y una vez más para crear una clase compuesta "MultiWriter", luego exponga una dependencia IWriter en su clase, configure el MultiWriter compuesto con los tres escritores reales, y enchúfelo. Ahora, es bastante SÓLIDO; a partir de este momento, siempre que los requisitos dicten que la salida debe ir a cualquier lugar nuevo, simplemente crea un nuevo IWriter y lo conecta al MultiWriter, sin necesidad de tocar ningún código de trabajo existente.

    
respondido por el KeithS 06.09.2011 - 17:46
fuente
17
  

el código bueno que solo puede hacer A es peor que el código malo que puede hacer A, B,   C, D.

1) Tenga un código que solo haga lo que se supone que debe hacer.

  

Si siento que es probable que esta pieza de código se agrande en el   futuro más cercano, pensaré en agregar abstracciones, moviendo esto   funcionalidad en otro lugar y así sucesivamente.

2) Si planifica su código para hacer A, B, C y D, el cliente finalmente le pedirá una E.

Su código debe hacer lo que se supone que debe hacer, no debe pensar ahora en futuras implementaciones, ya que nunca terminará cambiando su código continuamente, y lo más importante es diseñar el código en exceso. Debes refactorizar tu código tan pronto como sientas que lo necesita debido a las características actuales, no luchando por prepararlo para algo que aún no va a hacer, a menos que, por supuesto, lo hayas planeado como parte de la arquitectura del proyecto.

Le sugiero que lea un buen libro: The Pragmatic Programmer . Abrirá tu mente y te enseñará lo que debes hacer y lo que no debes hacer.

También Code Complete es un gran recurso lleno de información útil que todo desarrollador (no solo el programador) debe leer.

    
respondido por el Jose Faeti 06.09.2011 - 20:21
fuente
12
  

Necesito escribir un código simple, creo que en el futuro

Quizás este es el problema.

En las primeras etapas, no tienes idea de cuál sería el producto final. O si lo tienes, está mal. Sin lugar a duda. Es como un niño de 14 años que preguntó hace unos días en Programmers.SE si debe, para su futura carrera, elegir entre aplicaciones web y no recuerdo qué más: es bastante obvio que en pocos años, las cosas Él cambiará, le interesarán otros dominios, etc.

Si, para escribir tres líneas de código, creas una nueva interfaz y dos clases, estás sobre ingeniería. Obtendrá un código que será difícil de mantener y difícil de leer , solo porque por cada línea de código útil, tiene dos líneas de código que no necesita. Sin contar la documentación XML, pruebas unitarias, etc.

Piénselo: si quisiera saber cómo se implementa una característica en su código, ¿sería más fácil para mí leer veinte líneas de código, o sería más rápido y más fácil tener que abrir docenas de semi- Clases e interfaces vacías para descubrir cuál usa cuáles, cómo están relacionadas, etc.?

Recuerde: una base de código más grande significa más mantenimiento. No escriba más código cuando pueda evitarlo.

Su enfoque también es perjudicial en otros lados:

  • Si necesita eliminar una función, ¿no es más fácil averiguar dónde se usa un método específico de veinte líneas, que perder el tiempo para comprender las dependencias entre docenas de clases?

  • Al depurar, ¿no es más fácil tener un pequeño seguimiento de pila, o prefiere leer docenas de líneas para descubrir qué se llama por qué?

Para concluir, parece similar a optimización prematura . Usted está tratando de resolver el problema sin siquiera estar seguro de si hay un problema en primer lugar y dónde está. Cuando trabaje en la versión 1 de su producto, implemente las características que necesita implementar ahora mismo; no piense en algo que espera implementar en dos años en la versión 14.

    
respondido por el MainMa 06.09.2011 - 12:33
fuente
5

Escribir un montón de código que (probablemente) nunca se usará es una muy buena manera de obtener un P45 . No tienes una bola de cristal y no tienes idea de la dirección final que tomará el desarrollo, así que dedica tiempo a estos Las funciones solo cuestan dinero sin retorno.

    
respondido por el uɐɪ 06.09.2011 - 12:29
fuente
3

Tratar de predecir lo que puede necesitarse a partir del código en el futuro a menudo termina siendo una ingeniería excesiva innecesaria (un hábito que actualmente estoy tratando de sacudir). Yo diría que acaba de hacer las tres líneas. Cuando surge la necesidad (y no antes), refactoriza. De esa manera, su código siempre hace lo que necesita sin ser demasiado complicado y evoluciona de forma correcta a través de la refactorización.

    
respondido por el Gary Buyn 06.09.2011 - 12:25
fuente
3

A menudo digo que la codificación es como el lado claro / lado oscuro de la Fuerza: el lado "claro" requiere más esfuerzo pero produce mejores resultados. El lado "oscuro" es rápido y fácil y brinda mayores beneficios de inmediato, pero lo corrompe en el futuro. Una vez que comiences por el camino oscuro, siempre dominará tu destino.

Me encuentro con esto todo el tiempo, en cada trabajo que he tenido, es como una maldición de la que no puedo escapar. La cultura de la empresa es siempre el camino del lado oscuro, y los hacks / correcciones rápidas para eliminar nuevas funciones, y mis súplicas y lamentos de refactorización y escritura de códigos caen en los oídos sordos, si no es así. Conduje a mi terminación por "tratar de cambiar las cosas" (no es broma, eso sucedió varias veces, porque quería introducir patrones de diseño y alejarme de los ataques rápidos).

La triste verdad es que, a menudo, el camino del lado estúpido / oscuro es la forma en la que tienes que andar, solo debes asegurarte de hacerlo ligeramente. Poco a poco me he dado cuenta de que los programadores que entienden la forma correcta de escribir software, es decir, seguir SOLID, usar patrones de diseño, obedecer a SoC, etc. son mucho menos comunes que los piratas informáticos que escribirán un La declaración if para corregir un error, y cuando surgen más errores, simplemente agregue esa declaración en lugar de pensar "Quizás haya una mejor manera de hacerlo" y refactorice el código para que sea extensible adecuadamente.

    
respondido por el Wayne Molina 06.09.2011 - 14:25
fuente
3

Ser consciente de lo que podría pasar (futuro) NUNCA es malo. Pensar en qué podría ser una mejor manera de hacer algo es parte de lo que lo hace bueno en su trabajo. La parte más difícil es determinar si el tiempo empleado: la relación de pago está justificada. Todos hemos visto situaciones en las que las personas hacen lo "fácil si" para detener el sangrado inmediato (y / o gritar), y como se acumulan, se obtiene un código confuso. Muchos de nosotros también hemos experimentado una abstracción exagerada que es un misterio una vez que el codificador original se mueve, lo que también produce un código confuso.

Miraría tu situación y te haría estas preguntas:

  1. ¿Es este código algo que es de misión crítica, y será ¿Mucho más estable si vuelvo a codificar? En cirugía habla, es esto refactorización para salvar vidas, ¿o es meramente electiva y estética?

  2. ¿Estoy considerando el código de refactorización que vamos a reemplazar en 6 meses?

  3. ¿Estoy dispuesto a tomarme tanto tiempo para documentar el diseño y mi razonar para los futuros desarrolladores como estoy para gastar haciendo el ¿refactorización?

  4. Respecto a mi diseño elegante para agregar características futuras, es esto en El código al que los usuarios solicitan cambios cada semana, o es este el primer ¿Lo he tocado este año?

Hay momentos en los que YAGNI y KISS ganan el día, pero hay días en los que un cambio fundamental te llevará de la espiral descendente a la mierda. Mientras sea realista en su evaluación no solo de lo que quiere, sino también de lo que otros deberán hacer para mantener su trabajo, podrá determinar mejor cuál es cuál es la situación. Ah, y no olvides escribir lo que hiciste y por qué. Salvará a los que te siguen, pero también a ti mismo cuando tengas que volver más tarde.

    
respondido por el Jennifer S 06.09.2011 - 15:29
fuente
3

En la segunda edición de Stroustrups 'El lenguaje de programación C ++', (no tengo la página disponible) leí

  

No agregue código espontáneamente en su lugar.

y me fui bien siguiendo el consejo. Por supuesto, hay concesiones, y tienes que encontrar un equilibrio, pero los fragmentos cortos son más fáciles de probar que un gran desorden de espaguetis.

A menudo experimenté que, al diferenciar de un caso a dos casos, si consideras 2 como n-casos, abres una puerta a muchas nuevas posibilidades, en las que quizás no hayas pensado.

Pero luego tienes que hacer la pregunta de YAGNI: ¿Vale la pena? ¿Será realmente útil? Ser experimentado significa que rara vez te equivocarás y, como principiante, más a menudo te equivocarás.

Debe ser lo suficientemente crítico como para reconocer un patrón y detectar si su código es difícil de mantener, debido a demasiada abstracción o difícil de mantener, porque todo se resuelve en su lugar.

La solución no es esto o aquello, sino pensar en ello. :)

    
respondido por el user unknown 06.09.2011 - 17:08
fuente
2

"el código bueno que solo puede hacer A es peor que el código malo que puede hacer A, B, C y D."

Esto puede tener algún sentido en el desarrollo del producto; Pero la mayoría de los profesionales de TI trabajan en "proyectos" en lugar de desarrollo de productos.

En 'proyectos de TI', si programa un buen componente, funcionará sin problemas durante el tiempo de vida del proyecto, que no puede durar más de 5 o 10 años, para entonces el escenario de negocios podría haber quedado obsoleto y habrá una nueva Proyecto / producto ERP podría haberlo reemplazado. Durante este período de vida de 5/10 años, a menos que haya defectos en su código, nadie notará su existencia y los méritos de sus mejores pensamientos pasan desapercibidos. (a menos que seas bueno apostando fuerte tu propia trompeta)

¡No muchos tienen la oportunidad de programar el 'Windows Ctl + Alt + Del' y esos pocos tienen la oportunidad de no darse cuenta del potencial futuro de su código!

    
respondido por el Jose 25.11.2011 - 10:09
fuente
1

Muchos libros sobre desarrollo lean y / o ágil ayudarán a reforzar este enfoque: haga lo que sea necesario en este momento. Si sabe que está construyendo un marco, agregue abstracciones. De lo contrario, no agregue complejidad hasta que lo necesite. Recomiendo Lean Software Development , que presentará muchos otros conceptos que pueden marcar una gran diferencia en la productividad.

    
respondido por el sfuqua 06.09.2011 - 15:45
fuente
1

Es curioso cómo las personas hablan de la manera correcta / manera incorrecta de hacer las cosas. Al mismo tiempo, la tarea de programación sigue siendo muy difícil y no ofrece buenas soluciones para escribir grandes sistemas complejos.

Puede ser que algún día nosotros, los programadores, finalmente descubramos cómo escribir software complejo. Hasta entonces le sugiero que siempre comience con la implementación del prototipo "estúpido" primero, y luego dedique el tiempo suficiente a refactorizar para que sus universidades puedan seguir su código.

    
respondido por el AareP 06.09.2011 - 16:04
fuente
1

Habiendo visto diseños tan generalizados prematuramente que no se ajustaban en absoluto a los requisitos reales que surgieron más adelante, ideé una regla para mí:

Para requisitos hipotéticos, solo escriba código hipotético.

Esto es: le recomendamos que piense en los cambios que podrían ocurrir más adelante. Pero solo use estas ideas para elegir un diseño para el código que se pueda cambiar y refactorizar fácilmente si esos requisitos realmente surgen. Incluso puede escribir algún código en su cabeza (código hipotético) que querría escribir en ese caso, ¡pero no escriba ningún código real!

    
respondido por el Hans-Peter Störr 12.09.2011 - 22:08
fuente
0

Creo que la mentalidad que lo ayudará es esforzarse siempre por buscar soluciones concretas para codificar problemas en lugar de soluciones abstractas. Las abstracciones solo deben agregarse cuando en realidad ayudan a simplificar la base del código (cuando, por ejemplo, le permiten reducir la base del código).

Muchos programadores han descubierto que las abstracciones surgen casi por su cuenta, cuando DRY suben su código. Los patrones de diseño y las mejores prácticas lo ayudan a encontrar oportunidades para hacerlo, pero no son objetivos que valgan la pena perseguir.

    
respondido por el KaptajnKold 06.09.2011 - 16:32
fuente
0

Pienso que la ingeniería excesiva a menudo parece debida a la inseguridad acerca de escribir código. Todos los principios y patrones abstractos deben tratarse como herramientas para ayudarlo. Lo que sucede a menudo es que se tratan como estándares, uno debe cumplir.

Creo que un programador siempre está en una mejor posición para decidir cómo abstraerse que un axioma.

KeithS ya ha dicho el resto

    
respondido por el sabof 08.09.2011 - 00:14
fuente
0

Pregúntese cuáles son las ventajas de un buen diseño:

  • Más fácil de entender
  • Mantiene más fácil
  • portátil
  • Permanece útil por mucho tiempo
  • Es fácil agregar nuevas funciones

Ahora, pregúntese si agregar todas esas capas de abstracciones realmente se agrega a cualquiera de los puntos mencionados anteriormente. Si no lo hace, lo estás haciendo ERRÓN .

Si puedes agregar nuevas funciones agregando 3 líneas de código como esta:

if (condition()) {
  doSomething()
}

Entonces, por favor, por favor hazlo. Esto indica que su diseño anterior fue bueno y fácil de adaptar. Solo cuando tus clases comiencen a crecer más allá de cierta extensión, usa refactoring para dividir funciones y posiblemente extraer nuevas clases.

Mi regla de oro es que las nuevas características deben implementarse de la manera más minimalista posible, solo cuando hay algo demasiado grande que entender por adelantado (digamos, se necesita más de 1 día o medio día para implementar). diseño. A partir de ahí, solo agregue capas de abstracción cuando el tamaño del código crezca. ¡Entonces refactoriza después! Después de un tiempo, incluso debería resultarle natural cuando necesite diseñar un poco más o ir por el camino rápido. Otra indicación de que algún código puede usar algo de limpieza es cuando lo reutiliza. Cada vez que agregue una nueva función, o llame al código antiguo en un lugar nuevo, es un buen momento para mirar el código antiguo y ver si puede mejorarlo un poco antes de agregarlo nuevamente. De esta manera, el código de acceso rápido se volverá más limpio, mientras que las partes poco interesantes se pudrirán lentamente y no le quitarán su valioso tiempo. (Si usa algún código antiguo y no lo entiende rápidamente, esto podría ser una desmotivación para mejorarlo, pero recuerde que también es una gran advertencia que es apropiado realizar una limpieza / rediseño).

Si trabajas así, nunca diseñarás nada en exceso. Es posible que se requiera cierta disciplina para volver al código antiguo cuando desee agregar algo nuevo, o dejar el código nuevo un poco más feo de lo que le gusta, pero estará trabajando para lograr algo productivo en lugar de un exceso de ingeniería.

    
respondido por el Dorus 28.07.2017 - 15:36
fuente

Lea otras preguntas en las etiquetas