¿Qué se prueba como una buena longitud máxima de una función? [cerrado]

44

¿La duración de la función afecta la productividad de un programador? Si es así, ¿cuál es un buen número máximo de líneas para evitar la pérdida de productividad?

Dado que este es un tema muy testarudo, haga una copia de seguridad del reclamo con algunos datos.

Peter Mortensen
fuente
66
La longitud no debe medirse en LOC, sino en el tiempo necesario para comprender con precisión lo que hace. Y esa longitud, no debe ser más de un minuto. Si no puedo resolverlo en unos segundos, probablemente esté haciendo demasiado ... después de un minuto, definitivamente lo es.
CaffGeek
13
La duración máxima debe ser 17.
ThomasX
1
Piense S en SÓLIDO.
Kris Krause
1
@CaffGeek O tal vez la función simplemente está haciendo algo no trivial. He visto funciones que me llevarían días comprender completamente. Incluso las funciones donde entiendo todos los conceptos involucrados pueden tomar fácilmente media hora para trabajar en los detalles. Si bien es bueno tener funciones triviales, muchos problemas son simplemente inherentemente difíciles.
CodesInChaos

Respuestas:

46

Desde que me embarqué en esta locura en 1970, he visto exactamente un módulo que realmente necesitaba tener más de una página impresa (alrededor de 60 líneas). He visto muchos módulos que eran más largos.

Para el caso, he escrito módulos que eran más largos, pero generalmente eran grandes máquinas de estados finitos escritos como grandes declaraciones de cambio.

Parte del problema parece ser que a los programadores de estos días no se les enseña a modularizar las cosas.

Los estándares de codificación que maximizan el desperdicio de espacio vertical también parecen ser parte del problema. (Todavía no he conocido a un administrador de software que haya leído la " Psicología de la programación de computadoras " de Gerald Weinberg . Weinberg señala que varios estudios han demostrado que la comprensión del programador se limita esencialmente a lo que el programador puede ver en un instante dado. el programador tiene que desplazarse o pasar una página, su comprensión cae significativamente: tienen que recordar y hacer un resumen).

Sigo convencido de que muchas de las ganancias de productividad del programador bien documentadas de FORTH se debieron al sistema de "bloques" FORTH para el código fuente: los módulos estaban limitados a un máximo absoluto de 16 líneas de 64 caracteres. Podrías factorizar infinitamente, pero bajo ninguna circunstancia podrías escribir una rutina de 17 líneas.

John R. Strohm
fuente
3
Toda la filosofía de FORTH fue diseñada para fomentar esto ... Tenías la intención de diseñar tu propio vocabulario, dividiendo implacablemente tu programa en partes cada vez más pequeñas, terminando con menos guión y más diccionario. Los límites de longitud por sí solos no lo hacen: verá una rutina dividida en partes arbitrarias solo para satisfacer algún estándar de codificación. Creo que tiene toda la razón al sospechar que a los programadores simplemente "no se les enseña a modularizar las cosas"; Esta debería haber sido una de las grandes victorias de OOP, pero por varias razones a menudo se le desestima como un objetivo en sí mismo.
Shog9
1
@Señor. CRT: Los límites de longitud en las implementaciones FORTH orientadas a bloques modularización FORZADA. La naturaleza interactiva de la mayoría de los FORTH ayuda, al alentar a los módulos pequeños y probarlos rápidamente. D85, un FORTH basado en archivos, no forzó la modularización, y vi a los muchachos que jugaban con D85 escribir una gran cantidad de módulos para siempre de la corriente del programador de conciencia para siempre. De ahí mi convicción. (Por lo que vale, Liz Rather no está de acuerdo conmigo. Ella cree que es principalmente la interactividad lo que le da a FORTH el aumento de la productividad.)
John R. Strohm
2
+1 para presentarme el gran libro "Psicología de la programación informática" :)
Samuel
30

¿Cuál es el tamaño correcto, realmente?

Depende del idioma que use, pero en general (y para mi gusto personal):

  • Idealmente , menos de 25 líneas.
  • Aceptablemente , menos de 35 líneas.

Si es más, entonces es algo a lo que necesito volver más tarde y volver a trabajar.

Pero de manera realista , cualquier tamaño que deba ser cuando necesite entregar algo y que tenga más sentido en el momento de escupirlo de esa manera, hace que a veces sea aún más fácil que alguien lo revise antes del envío. (pero aún así vuelvo más tarde).

(Recientemente, mi equipo ejecutó un programa en nuestra base de código: encontramos clases con 197 métodos y otro con solo 3 métodos, pero uno de ellos tenía 600 líneas. Juego lindo: ¿cuál es el peor de los 2 males?)


Ahora para una respuesta más zen ... En general, se considera una buena práctica (TM) citar a uno o dos grandes hombres, así que aquí va:

Todo debe hacerse lo más simple posible, pero no más simple. - A. Einstein

La perfección finalmente se alcanza no cuando ya no hay nada que agregar, sino cuando ya no hay nada que quitar. - A. de Saint Exupéry


Anexo sobre estilos de comentarios

Como anexo a esto, sus funciones deben tener nombres claros que expliquen su intención. Con respecto a los comentarios, generalmente no hago comentarios dentro de una función:

  • los comentarios dicen "¿por qué?" ,
  • el código dice "¿cómo?" .

Un bloque de comentarios en la parte superior de cada función (que requiere explicación) es suficiente. Si su función es pequeña y los nombres de las funciones son lo suficientemente explícitos, entonces solo debe decir qué quiere lograr y por qué. Utilizo comentarios en línea solo para los campos en algunos idiomas o en los inicios de bloque para funciones que rompen esas reglas de línea 25-35 si la intención no está clara. Utilizo un comentario de bloque dentro del código cuando se produce una situación excepcional (un bloque de captura donde no necesita o no desea hacer nada debería tener un comentario que diga por qué, por ejemplo).

Para obtener más información, lea mi respuesta sobre Estilo y recomendaciones de código de comentarios

haylem
fuente
@haylem Supongo que esta es la versión del programador de Freddy vs. Jason :-)
Gaurav
Estoy de acuerdo, sin embargo, agregaría que debe tener el tamaño de una página de pantalla. Si tiene 72 líneas en una página de pantalla, la función no debe exceder las 72 líneas.
Nombre para mostrar el
@ Hay una cuestión de garantizar que una sola función se mantenga en una página de pantalla y no desplazarse, pero esa no es mi principal preocupación. Mi preocupación es la cantidad de tiempo que lleva procesar la información al leer una función, y es casi instantánea si está claramente escrita en 25 líneas. Se trata solo de seguir las llamadas a funciones. 72 es demasiado grande para mí (además, ¿qué pasa si tienes pantallas divididas? Y eso dependería de la fuente. Pero estoy de acuerdo con el valor histórico de la recomendación)
haylem
1
Por supuesto, a veces tiene funciones en las que simplemente copia 70 campos diferentes en 70 lugares diferentes. Principalmente uso ttpara generar estos, pero a veces estás atrapado con una función de culo largo (o una función de culo larga) que de todos modos no hace nada de interés, así que no es un problema real.
configurador
1
Cuando la función es, por ejemplo Map(x => x.Property1); Map(x => x.Property2); Map(x => x.Property3);, está claro que todo es lo mismo. (Tenga en cuenta que esto es solo un ejemplo; este tipo de función aparece de vez en cuando)
configurador
12

En mi opinión, cada función debe ser lo más pequeña posible. Cada función debe hacer solo una cosa y hacerlo bien. Eso realmente no responde a la pregunta de duración máxima, pero es más mi opinión sobre la duración de las funciones.

Para usar las palabras del tío Bob, "Extrae hasta que ya no puedas extraer nada más. Extrae hasta que caigas".

Jason
fuente
2
¿Qué quieres decir con tan pequeño como sea posible? ¿No sería lo más pequeño posible que cada función tuviera solo dos líneas: una para hacer una sola operación y otra que llama a una función para hacer el resto?
Kelmikra
Tío Bob otra vez. Piensa por ti mismo. No escuches a los tíos. Te llevan por mal camino.
gnasher729
10

¿Cuál debería ser la altura máxima de un edificio? Depende de dónde esté la construcción o de la altura que desee que tenga.
Puede obtener diferentes respuestas de diferentes personas que provienen de diferentes ciudades.
Algunas funciones de script y controladores de interrupción del kernel son muy largas.

Huang F. Lei
fuente
Estoy totalmente de acuerdo contigo. Me gustan las metáforas. :) Un arquitecto tonto puede haber construido un edificio de tres pisos que no sabe dónde colocar una salida de seguridad válida y otro edificio podría tener diez pisos y ser un diseño arquitectónico perfecto. Siempre debemos tener en cuenta que la legibilidad y la facilidad de mantenimiento deben ser la razón principal para refactorizar un método para reducir su tamaño y no el tamaño en sí. No se podría construir una ciudad con el 90% de los rascacielos, excepto en las películas de ciencia ficción. :)
Samuel
10

Un método que funciona para mí es: ¿puedo hacer que una parte de una función más larga dé un nombre que tenga sentido? Creo que la longitud de un método no es tan importante como un buen nombre. El método debe hacer lo que dice el nombre, ni más ni menos. Y deberías poder dar un buen nombre. Si no puede nombrar su método como bueno, el código probablemente no sea bueno en conjunto.

Mnementh
fuente
Y su método debe hacer solo una cosa para tener un buen nombre ... no 'Y', 'O' o cualquier otra cosa que haga que el método nombre 50 caracteres.
Samuel
10

Mientras sea necesario para hacer lo que tiene que hacer, pero ya no.

Paul Nathan
fuente
como dicen en c, "No hay ningún problema en c que no pueda resolver agregando otro puntero a ese puntero". siempre podría agregar otra función debajo, su pregunta sería a su epifanía "¿dónde termina?"
Nombre para mostrar
1
De hecho, lo voltearía y diría "tan corto como sea necesario, pero no más corto", pero obtienes mi +1 por estar lo suficientemente cerca :)
Ben Hughes
6

Creo que hay una compensación. Si tiene muchos métodos cortos, a menudo es más difícil depurarlos que un método largo. Si tiene que saltar alrededor del editor 20 o 30 veces diferentes para rastrear una llamada de método, será difícil mantenerlo todo en su cabeza. Mientras tanto, si hay un método claro bien escrito, incluso si son 100 líneas, a menudo es más fácil mantenerlo en la cabeza.

La verdadera pregunta es por qué los elementos deberían estar en diferentes métodos, y la respuesta que se da arriba es la reutilización del código. Si no está reutilizando el código (o no lo sabe), entonces puede tener sentido dejarlo en un método gigante fácil de seguir y luego, cuando necesite reutilizarlo, divida las partes que necesitan utilizando en métodos más pequeños.

En realidad, parte del buen diseño del método es hacer métodos funcionalmente coherentes (esencialmente hacen una cosa). La longitud de los métodos no importa. Si una función hace una cosa bien definida y tiene 1,000 líneas, entonces es un buen método. Si una función hace 3 o 4 cosas y tiene solo 15 líneas, entonces es un mal método ...

Cervo
fuente
Me gustan los métodos cortos.
Marcie
Me gusta lo que dijiste porque decir que un método no debe tener más de 10 líneas es, en mi opinión, una utopía. Ok, es una buena regla tener en cuenta cada vez que escribes un método, pero no debe ser una regla matemática como 1 + 1 = 2. Si respetas principios como KISS, DRY, YAGNI, etc ... y tus métodos son No hay comentarios completos que expliquen algunos detalles porque son demasiado largos, los métodos pueden tener 100 líneas de código y pueden ser totalmente limpios de entender y mantener. Sin embargo, debería ser más una excepción que un hábito. Creo que cambiar el caso en el método de fábrica es un buen ejemplo de excepción.
Samuel
5

Me resulta más fácil hacer un seguimiento de lo que estoy haciendo si puedo ver toda la función a la vez. Así que así es como prefiero escribir funciones:

  1. Lo suficientemente corto como para caber en mi monitor con una fuente razonable.
  2. Si necesita ser más largo que el n. ° 1, lo suficientemente corto como para imprimir en una hoja de papel con una fuente razonable.
  3. Si necesita ser más largo que el n. ° 2, lo suficientemente corto como para imprimir 2 en una hoja de papel.

Raramente escribo funciones más largas que eso. La mayoría de ellos son declaraciones de conmutador C / C ++ gigantes.

Bob Murphy
fuente
Es lo suficientemente corto como para caber en un monitor, pero especifica el tipo y tamaño de fuente. El papel no debería ser una regla desde mi punto de vista porque estamos en 2013 y quién todavía imprime el código en papel, ¿quién imprimirá solo para ver si cabe en un tamaño de papel? Con herramientas como Visual Studio, intellisense, ya no hay razón para analizar el código con papel.
Samuel
5

Para mí, una función tiene la longitud que necesita. La mayoría de las veces que lo divido es cuando reutilizaré el código.

Básicamente, me atendré al principal 'alta cohesión, bajo acoplamiento' y no hay restricción en la longitud.

Ross
fuente
5

La pregunta debe ser cuántas cosas debe hacer una función. Y generalmente, es raro que necesite 100 líneas para hacer "una" cosa. Una vez más, eso depende del nivel desde el que esté mirando el código: ¿una clave es el hashing de una contraseña? ¿O es hashing y guardar la contraseña una cosa?

Yo diría que empiece guardando la contraseña como una función. Cuando sienta que el hash es diferente y refactorice el código. No soy un programador experto de ninguna manera, pero en mi humilde opinión, la idea general de las funciones comienza pequeña es que cuanto más atómicas sean sus funciones, mayores serán las posibilidades de reutilización del código, sin tener que hacer el mismo cambio en más de un lugar etc.

He visto procedimientos almacenados de SQL que se ejecutan en más de 1000 líneas. ¿El número de líneas de procedimientos almacenados también es inferior a 50? No lo sé, pero hace que leer el código sea un infierno. No solo tiene que seguir desplazándose hacia arriba y hacia abajo, sino que debe asignar a algunas líneas de código un nombre como "esto valida1", "esto actualiza en la base de datos", etc., un trabajo que el programador debería haber hecho.

Narayana
fuente
+1 solo para el primer párrafo. todo lo demás es relativo al caso en el que está trabajando.
Nombre para mostrar
¿Y cómo ayuda dividirlo en 50 funciones? Cuando las funciones tienen que comunicarse, ¿no terminarás con 500 líneas sino con mil?
gnasher729
5

De la complejidad ciclomática (Wikipedia):

La complejidad ciclomática de una sección del código fuente es el recuento del número de rutas linealmente independientes a través del código fuente.

  • Recomiendo que mantenga ese número por debajo de 10 en un solo método. Si llega a 10, entonces es hora de re-factorizar.

  • Existen herramientas que pueden evaluar su código y darle un número de complejidad ciclomática.

  • Debe esforzarse por integrar estas herramientas en su canal de compilación.

  • No persigas literalmente el tamaño de un método, pero trata de ver su complejidad y responsabilidades. Si tiene más de una responsabilidad, probablemente sea una buena idea re-factorizar. Si su complejidad ciclomática aumenta, probablemente sea hora de re-factorizar.

  • Estoy bastante seguro de que hay otras herramientas que le brindan comentarios similares, pero aún no tuve la oportunidad de analizar esto.

CodeART
fuente
Se ha demostrado que la complejidad ciclomática, en el código real de uno de los grandes repositorios públicos, no en ejemplos artificiales, está muy relacionada con SLOC sin procesar. Esto lo hace básicamente inútil, ya que es mucho más fácil contar los retornos de carro. (Sí, es posible jugar SLOC. Sea honesto, aquí: ¿Cuánto tiempo se le permitiría a alguien que estaba jugando las métricas de SLOC en su empleador continuar dibujando un cheque de pago?)
John R. Strohm
4

Por lo general, trato de mantener mis métodos / funciones a lo que cabe en la pantalla de un monitor de 1680x1050. Si no encaja, utilice métodos / funciones auxiliares para repartir la tarea.

Ayuda a la legibilidad tanto en pantalla como en papel.

Jason
fuente
Hago lo mismo pero vale la pena especificar qué tipo de fuente y tamaño está utilizando. Para mí, prefiero "consolas" con un tamaño de 14 como lo sugiere Scott Hanselman. hanselman.com/blog/… La primera vez es difícil trabajar con una fuente tan grande, pero es una buena práctica recordar siempre que su método debe ser lo más pequeño posible.
Samuel
4

No pongo un límite de línea dura a nada porque algunas funciones implementan algoritmos que son intrínsecamente complejos y cualquier intento de acortarlos haría las interacciones entre las nuevas funciones más cortas tan complicadas que el resultado neto no sería una reducción en la simplicidad. Tampoco creo que la idea de que una función solo haga "una cosa" es una buena guía, ya que "una cosa" en un alto nivel de abstracción puede ser "muchas cosas" en un nivel inferior.

Para mí, una función es definitivamente demasiado larga si su longitud causa violaciones sutiles de DRY en este momento, y extraer parte de la función en una nueva función o clase podría resolver esto. Una función puede ser demasiado larga si este no es el caso, pero se podría extraer fácilmente una función o clase que haría que el código sea más modular de una manera que probablemente sea útil ante el cambio previsible en el futuro.

dsimcha
fuente
Las funciones hacen "una cosa" en un nivel de abstracción específico, y solo te preocupas por ese nivel de abstracción. Ese es todo el punto. Si no puedes entender eso, entonces no creo que entiendas la abstracción.
Zoran Pavlovic
4

Suficientemente corto para ser optimizado correctamente

Los métodos deben ser tan cortos como para hacer exactamente una cosa. La razón de esto es simple: para que su código se pueda optimizar correctamente.

En un lenguaje JIT-ted como Java o C #, es importante que sus métodos sean simples para que el compilador JIT pueda producir código rápidamente. Los métodos más largos y complicados requieren naturalmente más tiempo de JIT. Además, los compiladores JIT solo ofrecen un puñado de optimizaciones y solo los métodos más simples se benefician de esto. Este hecho incluso se mencionó en el C # efectivo de Bill Wagner .

En un lenguaje de nivel inferior, como C o C ++, tener métodos cortos (tal vez una docena de líneas) también es importante porque de esa manera minimiza la necesidad de almacenar variables locales en RAM en lugar de en un registro. (También conocido como 'Registro de derrames'). Sin embargo, tenga en cuenta que en este caso no administrado, el costo relativo de cada llamada de función puede ser bastante alto.

E incluso en un lenguaje dinámico, como Ruby o Python, tener métodos cortos también ayuda en las optimizaciones del compilador. En un lenguaje dinámico, cuanto más 'dinámica' es una característica, más difícil es optimizarla. Por ejemplo, un método largo que toma una X y podría devolver un Int, Float o String probablemente funcionará mucho más lento que tres métodos separados, cada uno de los cuales solo devuelve un solo tipo. Esto se debe a que, si el compilador sabe exactamente qué tipo devolverá la función, también puede optimizar el sitio de llamada de la función. (Por ejemplo, sin verificar las conversiones de tipos).

Chris Smith
fuente
Alrededor del 99.999% de las aplicaciones que existen tienen muchas más cosas que reducen la velocidad de los programas, como el acceso a la base de datos, el acceso a los archivos o la latencia de la red. Pensar en la velocidad al diseñar métodos podría ser una razón válida para jugar, aplicar en tiempo real o informar con toneladas de datos, pero no en otros casos. Sin embargo, es un buen punto, pero por pequeño que sea como programador, rara vez tengo que hacer este tipo de optimización en mis aplicaciones.
Samuel
Hace ese tipo de cosas cuando mide la velocidad de su código y lo encuentra demasiado lento, si sabe lo que está haciendo (no es tan simple como cree), y si lo mide después y la velocidad ha mejorado .
gnasher729
3

Depende mucho de lo que haya en el código.

He visto una rutina de mil líneas con la que no tuve ningún problema. Fue una declaración de cambio enorme, ninguna opción superó una docena de líneas y la única estructura de control en cualquier opción era un solo bucle. En estos días se habría escrito con objetos, pero esa no era una opción en ese momento.

También estoy mirando 120 líneas en un interruptor frente a mí. Ningún caso supera las 3 líneas: un guardia, una asignación y el descanso. Está analizando un archivo de texto, los objetos no son una posibilidad. Cualquier alternativa sería más difícil de leer.

Loren Pechtel
fuente
2

A la mayoría de los compiladores no les importa la longitud de una función. Una función debe ser funcional, pero ser fácil de entender, cambiar y reutilizar para los seres humanos. Elija la longitud que más le convenga.

LennyProgrammers
fuente
1

Mi regla general es que una función debe caber en la pantalla. Solo he encontrado tres casos que tienden a violar esto:

1) Funciones de despacho. En los viejos tiempos, estos eran comunes, pero la mayoría de ellos se reemplazan con la herencia de objetos en estos días. Sin embargo, los objetos solo funcionan dentro de su programa y, por lo tanto, aún verá funciones de despacho ocasionales al tratar con datos que llegan de otro lugar.

2) Funciones que hacen un montón de pasos para lograr un objetivo y donde los pasos carecen de una buena subdivisión. Terminas con una función que simplemente llama a una larga lista de otras funciones en orden.

3) Como # 2 pero donde los pasos individuales son tan pequeños que simplemente están en línea en lugar de llamarse por separado.

Loren Pechtel
fuente
1

Quizás la longitud de la función no sea tan buena métrica. Intentamos utilizar la complejidad ciclomática , también en los métodos, y una de las futuras reglas de control de origen que establece que la complejidad ciclomática en las clases y los métodos debe ser inferior a X.

Para los métodos, X se establece en 30, y eso es bastante ajustado.

Peter Mortensen
fuente