Mejores prácticas de codificación - Coding best practices

Las mejores prácticas de codificación son un conjunto de reglas informales que emplea la comunidad de desarrollo de software para ayudar a mejorar la calidad del software.

Muchos programas informáticos permanecen en uso durante largos períodos de tiempo, por lo que cualquier regla debe facilitar tanto el desarrollo inicial como el mantenimiento y la mejora posteriores por parte de personas distintas de los autores originales.

En la regla del noventa y noventa , a Tom Cargill se le atribuye una explicación de por qué los proyectos de programación a menudo se retrasan: "El primer 90% del código representa el primer 90% del tiempo de desarrollo. El 10% restante del código representa el otro 90% del tiempo de desarrollo ". Vale la pena considerar cualquier orientación que pueda corregir esta falta de previsión.

El tamaño de un proyecto o programa tiene un efecto significativo en las tasas de error, la productividad del programador y la cantidad de gestión necesaria.

Calidad del software

Como se enumeran a continuación, hay muchos atributos asociados con un buen software. Algunos de estos pueden ser mutuamente contradictorios (por ejemplo, ser muy rápido en comparación con realizar una verificación de errores extensa), y diferentes clientes y participantes pueden tener diferentes prioridades. Weinberg proporciona un ejemplo de cómo diferentes objetivos pueden tener un efecto dramático tanto en el esfuerzo requerido como en la eficiencia. Además, señala que los programadores generalmente apuntarán a lograr cualquier objetivo explícito que pueda establecerse, probablemente a expensas de cualquier otro atributo de calidad.

Sommerville ha identificado cuatro atributos generalizados que no se refieren a lo que hace un programa, sino a qué tan bien lo hace:

Weinberg ha identificado cuatro objetivos que debe cumplir un buen programa:

  • ¿Un programa cumple con su especificación ("salida correcta para cada entrada posible")?
  • ¿Se produce el programa según lo programado (y dentro del presupuesto)?
  • ¿Qué tan adaptable es el programa para hacer frente a los requisitos cambiantes?
  • ¿Es el programa lo suficientemente eficiente para el entorno en el que se utiliza?

Hoare ha identificado diecisiete objetivos relacionados con la calidad del software, que incluyen:

  • Definición clara de propósito.
  • Sencillez de uso.
  • Robustez (difícil de mal uso, amable con los errores).
  • Disponibilidad anticipada (entregada a tiempo cuando sea necesario).
  • Fiabilidad.
  • Extensibilidad a la luz de la experiencia.
  • Brevedad.
  • Eficiencia (lo suficientemente rápido para el propósito al que se destina).
  • Costo mínimo para desarrollar.
  • Conformidad con los estándares relevantes .
  • Documentos de usuario claros, precisos y precisos .

Prerrequisitos

Antes de que comience la codificación, es importante asegurarse de que se hayan completado todos los requisitos previos necesarios (o que al menos hayan progresado lo suficiente como para proporcionar una base sólida para la codificación). Si no se cumplen los distintos requisitos previos, es probable que el software no sea satisfactorio, incluso si está completo.

De Meek & Heath: "Lo que sucede antes de que uno llegue a la etapa de codificación es a menudo de crucial importancia para el éxito del proyecto".

Los requisitos previos que se describen a continuación cubren cuestiones tales como:

  • ¿Cómo se estructura el desarrollo? (ciclo vital)
  • ¿Qué está destinado a hacer el software? (requisitos)
  • la estructura general del sistema de software (arquitectura)
  • diseño más detallado de componentes individuales (diseño)
  • elección de lenguaje (s) de programación

Para proyectos pequeños y simples que involucran a una sola persona, puede ser factible combinar la arquitectura con el diseño y adoptar un ciclo de vida muy simple.

Ciclo vital

Una metodología de desarrollo de software es un marco que se utiliza para estructurar, planificar y controlar el ciclo de vida de un producto de software. Metodologías comunes incluyen la cascada , la creación de prototipos , iterativo e incremental de desarrollo , desarrollo en espiral , el desarrollo ágil de software , el desarrollo rápido de aplicaciones , y la programación extrema .

El modelo de cascada es un enfoque de desarrollo secuencial; en particular, asume que los requisitos se pueden definir completamente al inicio de un proyecto. Sin embargo, McConnell cita tres estudios que indican que, en promedio, los requisitos cambian alrededor de un 25% durante un proyecto. Las otras metodologías mencionadas anteriormente intentan reducir el impacto de tales cambios en los requisitos, a menudo mediante alguna forma de enfoque escalonado, incremental o iterativo. Diferentes metodologías pueden ser apropiadas para diferentes entornos de desarrollo.

Requisitos

McConnell afirma: "El primer requisito previo que debe cumplir antes de comenzar la construcción es una declaración clara del problema que se supone que debe resolver el sistema".

Meek y Heath enfatizan que una especificación escrita clara, completa, precisa e inequívoca es el objetivo al que aspirar. Tenga en cuenta que puede que no sea posible lograr este objetivo, y es probable que el objetivo cambie de todos modos (como se mencionó en la sección anterior).

Sommerville distingue entre requisitos de usuario menos detallados y requisitos de sistema más detallados. También distingue entre requisitos funcionales (por ejemplo, actualizar un registro) y requisitos no funcionales (por ejemplo, el tiempo de respuesta debe ser inferior a 1 segundo).

Arquitectura

Hoare señala: "hay dos formas de construir un diseño de software: una es hacerlo tan simple que obviamente no haya deficiencias; la otra forma es hacerlo tan complicado que no haya deficiencias obvias . El primer método es mucho más difícil ".

La arquitectura del software se ocupa de decidir qué se debe hacer y qué componente del programa lo hará (cómo se hace algo se deja para la fase de diseño detallado, a continuación). Esto es particularmente importante cuando un sistema de software contiene más de un programa, ya que define efectivamente la interfaz entre estos diversos programas. También debe incluir alguna consideración de las interfaces de usuario, sin entrar en detalles excesivos.

Cualquier requisito del sistema no funcional (tiempo de respuesta, confiabilidad, mantenibilidad, etc.) debe considerarse en esta etapa.

La arquitectura del software también es de interés para varias partes interesadas (patrocinadores, usuarios finales, etc.) ya que les da la oportunidad de comprobar que se pueden cumplir sus requisitos.

Diseño

El propósito principal del diseño es completar los detalles que se han pasado por alto en el diseño arquitectónico. La intención es que el diseño sea lo suficientemente detallado como para proporcionar una buena guía para la codificación real, incluidos los detalles de cualquier algoritmo particular que se utilice. Por ejemplo, a nivel arquitectónico, se puede haber notado que algunos datos deben ser ordenados, mientras que a nivel de diseño es necesario decidir qué algoritmo de ordenación se utilizará. Como ejemplo adicional, si se utiliza un enfoque orientado a objetos, entonces se deben determinar los detalles de los objetos (atributos y métodos).

Elección de lenguaje (s) de programación

Mayer afirma: "Ningún lenguaje de programación es perfecto. No existe ni un solo mejor lenguaje; solo hay lenguajes que se adaptan bien o tal vez no se adaptan bien a propósitos particulares. Es necesario comprender el problema y los requisitos de programación asociados para elegir el idioma que mejor se adapte a la solución."

De Meek & Heath: "La esencia del arte de elegir un idioma es comenzar con el problema, decidir cuáles son sus requisitos y su importancia relativa, ya que probablemente será imposible satisfacerlos todos igualmente bien. Los idiomas disponibles deberían entonces compararse con la lista de requisitos y elegir el más adecuado (o el menos insatisfactorio) ".

Es posible que diferentes lenguajes de programación sean apropiados para diferentes aspectos del problema. Si los lenguajes o sus compiladores lo permiten, puede ser factible mezclar rutinas escritas en diferentes lenguajes dentro del mismo programa.

Incluso si no hay elección sobre qué lenguaje de programación se utilizará, McConnell ofrece algunos consejos: "Cada lenguaje de programación tiene fortalezas y debilidades. Sea consciente de las fortalezas y debilidades específicas del lenguaje que está utilizando".

Estándares de codificación

Esta sección también es realmente un requisito previo para la codificación, como señala McConnell: "Establezca convenciones de programación antes de comenzar a programar. Es casi imposible cambiar el código para que coincida con ellas más tarde".

Como se enumeran cerca del final de las convenciones de codificación , existen diferentes convenciones para diferentes lenguajes de programación, por lo que puede ser contraproducente aplicar las mismas convenciones en diferentes lenguajes. Es importante tener en cuenta que no existe una convención de codificación en particular para ningún lenguaje de programación. Cada organización tiene un estándar de codificación personalizado para cada tipo de proyecto de software. Por lo tanto, es imperativo que el programador elija o cree un conjunto particular de pautas de codificación antes de que comience el proyecto de software. Algunas convenciones de codificación son genéricas y pueden no aplicarse a todos los proyectos de software escritos con un lenguaje de programación en particular.

El uso de convenciones de codificación es particularmente importante cuando un proyecto involucra a más de un programador (ha habido proyectos con miles de programadores). Es mucho más fácil para un programador leer el código escrito por otra persona si todo el código sigue las mismas convenciones.

Para algunos ejemplos de malas convenciones de codificación, Roedy Green proporciona un artículo extenso (irónico) sobre cómo producir código que no se puede mantener.

Comentando

Debido a restricciones de tiempo o programadores entusiastas que quieren resultados inmediatos para su código, los comentarios de código a menudo quedan en segundo plano. Los programadores que trabajan en equipo han descubierto que es mejor dejar comentarios, ya que la codificación generalmente sigue ciclos, o más de una persona puede trabajar en un módulo en particular. Sin embargo, algunos comentarios pueden reducir el costo de la transferencia de conocimientos entre los desarrolladores que trabajan en el mismo módulo.

En los primeros días de la informática, una práctica comentada era dejar una breve descripción de lo siguiente:

  1. Nombre del módulo
  2. Propósito del módulo
  3. Descripción del módulo
  4. Autor original
  5. Modificaciones
  6. Autores que modificaron el código con una descripción de por qué se modificó.

La "descripción del módulo" debe ser lo más breve posible pero sin sacrificar la claridad y la exhaustividad.

Sin embargo, los dos últimos elementos han quedado obsoletos en gran medida por la llegada de los sistemas de control de revisiones . Las modificaciones y su autoría se pueden rastrear de manera confiable usando tales herramientas en lugar de usando comentarios.

Además, si se está utilizando lógica complicada, es una buena práctica dejar un "bloque" de comentarios cerca de esa parte para que otro programador pueda entender qué está sucediendo exactamente.

Las pruebas unitarias pueden ser otra forma de mostrar cómo se pretende utilizar el código.

Convenciones de nombres

El uso de convenciones de nomenclatura adecuadas se considera una buena práctica. A veces, los programadores tienden a usar X1, Y1, etc. como variables y se olvidan de reemplazarlas por otras significativas, lo que genera confusión.

Por lo general, se considera una buena práctica utilizar nombres descriptivos.

Ejemplo: Una variable para tomar el peso como parámetro para un camión puede denominarse TrkWeight o TruckWeightKilograms, siendo la preferible TruckWeightKilograms, ya que se reconoce instantáneamente. Consulte Asignación de nombres de variables a CamelCase .

Mantenga el código simple

El código que escribe un programador debe ser simple. La lógica complicada para lograr algo simple debe mantenerse al mínimo, ya que el código podría ser modificado por otro programador en el futuro. La lógica que implementó un programador puede no tener perfecto sentido para otro. Por lo tanto, mantenga siempre el código lo más simple posible.

Por ejemplo, considere estas líneas equivalentes de código C:

if (hours < 24 && minutes < 60 && seconds < 60)
{
    return true;
}
else
{
    return false;
}

y

if (hours < 24 && minutes < 60 && seconds < 60)
    return true;
else
    return false;

y

switch (hours < 24 && minutes < 60 && seconds < 60){
    case true:
        return true;
    break;
    case false:
        return false;
    break;
    default:
        return false;
}

y

return hours < 24 && minutes < 60 && seconds < 60;

El primer enfoque, que se utiliza con mucha más frecuencia, es considerablemente más grande que el cuarto. En particular, consume 5 veces más espacio vertical de pantalla (líneas) y 97 caracteres frente a 52 (aunque las herramientas de edición pueden reducir la diferencia en la escritura real). Sin embargo, es discutible que sea "más simple". El primero tiene un if / then else explícito, con un valor de retorno explícito obviamente conectado con cada uno; incluso un programador novato no debería tener dificultades para entenderlo. El segundo simplemente descarta las llaves, cortando el tamaño "vertical" a la mitad con pocos cambios en la complejidad conceptual. En la mayoría de los idiomas, las declaraciones de "retorno" también se pueden agregar a las líneas anteriores, llevando el tamaño "vertical" a solo una línea más que la cuarta forma.

La cuarta forma obviamente minimiza el tamaño, pero puede aumentar la complejidad: deja implícitos los valores "verdadero" y "falso" y mezcla las nociones de "condición" y "valor de retorno". Es probable que sea obvio para la mayoría de los programadores, pero es posible que un principiante no comprenda de inmediato que el resultado de evaluar una condición es en realidad un valor (de tipo booleano o su equivalente en cualquier idioma) y, por lo tanto, puede manipularse o devolverse. En ejemplos más realistas, la cuarta forma podría tener problemas debido a la precedencia de los operadores , quizás devolviendo un tipo inesperado, donde las formas anteriores en algunos idiomas reportarían un error. Por tanto, la "simplicidad" no es meramente una cuestión de extensión, sino de estructura lógica y conceptual; acortar el código puede hacerlo menos o más complejo.

Para programas grandes y de larga duración, el uso de alternativas detalladas podría contribuir a la hinchazón .

La compacidad puede permitir que los codificadores vean más código por página, lo que reduce los gestos de desplazamiento y las pulsaciones de teclas. Dada la cantidad de veces que se puede ver el código en el proceso de escritura y mantenimiento, podría significar un ahorro significativo en las pulsaciones de teclas del programador durante la vida del código. Esto puede no parecer significativo para un estudiante que está aprendiendo a programar por primera vez, pero, al producir y mantener programas grandes, la reducción de la cantidad de líneas de código permite que quepa más código en la pantalla, la simplificación menor del código puede mejorar la productividad y también Reducir la fatiga ocular, de la muñeca y de los dedos, que son problemas médicos comunes que sufren los codificadores de producción y los trabajadores de la información.

La codificación Terser acelera la compilación muy ligeramente, ya que es necesario procesar menos símbolos. Además, el tercer enfoque puede permitir que líneas de código similares se comparen más fácilmente, particularmente cuando muchas de estas construcciones pueden aparecer en una pantalla al mismo tiempo.

Finalmente, los diseños muy concisos pueden utilizar mejor las modernas pantallas de computadora de pantalla ancha, dependiendo del diseño y la configuración del monitor. En el pasado, las pantallas estaban limitadas a 40 u 80 caracteres (tales límites se originaron mucho antes: manuscritos, libros impresos e incluso pergaminos, durante milenios han utilizado líneas bastante cortas (ver, por ejemplo, la Biblia de Gutenberg ). Las pantallas modernas pueden mostrar fácilmente 200 o más caracteres, lo que permite líneas extremadamente largas. La mayoría de los estilos y estándares de codificación modernos no ocupan todo el ancho. Por lo tanto, si se usa una ventana del ancho de la pantalla, se desperdicia una gran cantidad de espacio disponible. Por otro lado, con múltiples Windows, o utilizando un IDE u otra herramienta con información diversa en los paneles laterales, el ancho disponible para el código está en el rango familiar de los sistemas anteriores.

También vale la pena señalar que el sistema visual humano se ve muy afectado por la longitud de la línea; las líneas muy largas aumentan ligeramente la velocidad de lectura, pero reducen la comprensión. Columnas de texto: ¿Cuánto tiempo es demasiado largo? y se suman a los errores de seguimiento ocular. Algunos estudios sugieren que a las líneas más largas les va mejor en línea que en la versión impresa Human Factors International , pero esto todavía solo sube a unas 10 pulgadas, y principalmente para la velocidad bruta de lectura en prosa.

Portabilidad

El código del programa no debe contener valores "codificados" (literales) que se refieran a parámetros ambientales, como rutas de archivo absolutas, nombres de archivo, nombres de usuario, nombres de host, direcciones IP, URL, puertos UDP / TCP. De lo contrario, la aplicación no se ejecutará en un host que tenga un diseño diferente al previsto. Un programador cuidadoso puede parametrizar tales variables y configurarlas para el entorno de alojamiento fuera de la aplicación propiamente dicha (por ejemplo, en archivos de propiedades, en un servidor de aplicaciones o incluso en una base de datos). Compare el mantra de un "punto único de definición" (SPOD).

Como extensión, los recursos como los archivos XML también deben contener variables en lugar de valores literales; de lo contrario, la aplicación no será portátil a otro entorno sin editar los archivos XML. Por ejemplo, con aplicaciones J2EE ejecutándose en un servidor de aplicaciones, dichos parámetros ambientales se pueden definir en el alcance de la JVM y la aplicación debe obtener los valores de allí.

Escalabilidad

Diseñar código con la escalabilidad como objetivo de diseño porque, muy a menudo, en los proyectos de software, siempre se agregan nuevas características a un proyecto que se hace más grande. Por lo tanto, la posibilidad de agregar nuevas funciones a una base de código de software se convierte en un método invaluable para escribir software.

Reutilización

La reutilización es un objetivo de diseño muy importante en el desarrollo de software. La reutilización reduce los costes de desarrollo y también reduce el tiempo de desarrollo si los componentes o módulos que se reutilizan ya se han probado. Muy a menudo, los proyectos de software comienzan con una línea de base existente que contiene el proyecto en su versión anterior y, dependiendo del proyecto, muchos de los módulos y componentes de software existentes se reutilizan, lo que reduce el tiempo de desarrollo y prueba y, por lo tanto, aumenta la probabilidad de entregar un proyecto de software a tiempo. .

Pautas de construcción en breve

Una descripción general de todo lo anterior:

  1. Sepa lo que debe realizar el bloque de código
  2. Mantenga las convenciones de nomenclatura que sean uniformes en todas partes.
  3. Indique una breve descripción de para qué sirve una variable (referencia a comentar)
  4. Corrija los errores a medida que ocurren.
  5. Mantenga su código simple
  6. Diseñe código teniendo en cuenta la escalabilidad y la reutilización.

Desarrollo de código

Construcción de código

Una de las mejores prácticas para la creación de códigos implica compilaciones y pruebas diarias, o mejor aún, una integración continua , o incluso una entrega continua .

Pruebas

Las pruebas son una parte integral del desarrollo de software que debe planificarse. También es importante que las pruebas se realicen de forma proactiva; lo que significa que los casos de prueba se planifican antes de que comience la codificación, y los casos de prueba se desarrollan mientras se diseña y codifica la aplicación.

Depurar el código y corregir errores

Los programadores tienden a escribir el código completo y luego comienzan a depurar y verificar errores. Aunque este enfoque puede ahorrar tiempo en proyectos más pequeños, los más grandes y complejos tienden a tener demasiadas variables y funciones que necesitan atención. Por lo tanto, es bueno depurar todos los módulos una vez que haya terminado y no todo el programa. Esto ahorra tiempo a largo plazo para que uno no termine perdiendo mucho tiempo averiguando qué está mal. Las pruebas unitarias para módulos individuales y / o pruebas funcionales para servicios web y aplicaciones web pueden ayudar con esto.

Despliegue

La implementación es la etapa final del lanzamiento de una aplicación para los usuarios. Algunas de las mejores prácticas son:

  1. Mantenga la estructura de instalación simple: los archivos y directorios deben mantenerse al mínimo. No instale nada que nunca se vaya a utilizar.
  2. Conserve solo lo necesario: las actividades de administración de la configuración del software deben asegurarse de que esto se cumpla. Los recursos no utilizados (versiones antiguas o fallidas de archivos, código fuente, interfaces, etc.) deben archivarse en algún otro lugar para mantener las compilaciones más nuevas y optimizadas.
  3. Mantenga todo actualizado: las actividades de administración de la configuración del software deben asegurarse de que esto se cumpla. Para implementaciones basadas en delta, asegúrese de que las versiones de los recursos que ya están implementados sean las más recientes antes de implementar los deltas. Si no está seguro, realice una implementación desde cero (elimine todo primero y luego vuelva a implementar).
  4. Adopte una estrategia de varias etapas: según el tamaño del proyecto, a veces se necesitan más implementaciones.
  5. Tenga una estrategia de retroceso: debe haber una forma de retroceder a una versión anterior (en funcionamiento).
  6. Confíe en la automatización para procesos repetibles: hay demasiado margen para el error humano, las implementaciones no deberían ser manuales. Utilice una herramienta que sea nativa de cada sistema operativo o utilice un lenguaje de secuencias de comandos para implementaciones multiplataforma.
  7. Vuelva a crear el entorno de implementación real: considere todo (enrutadores, firewalls, servidores web, navegadores web, sistemas de archivos, etc.)
  8. No cambie los procedimientos de implementación y los scripts sobre la marcha y documente dichos cambios: espere una nueva iteración y registre dichos cambios de manera apropiada.
  9. Personalice la implementación: los productos de software más nuevos, como API, microservicios, etc., requieren consideraciones específicas para una implementación exitosa.
  10. Reduzca el riesgo de otras fases de desarrollo: si otras actividades como las pruebas y la administración de la configuración son incorrectas, la implementación seguramente fallará.
  11. Considere la influencia que tiene cada parte interesada: Consideraciones organizacionales, sociales y gubernamentales.

Ver también

Referencias

General

enlaces externos