Coding Standard Jarabesoft v1.2.0.

※ Archivos

  • Los archivos generados manualmente deben ser nombrados con Pascal Case ó Upper Camel Case seguido de su respectiva extensión, los nombres deben ser claros y descriptivos, por ejemplo:

    Correcto Incorrecto
    StoreInventory.ts StrInv.ts
  • En el caso de utilizar los frameworks del stack actual (Angular, Loopback y Ionic) los archivos generados deberán respetan el nombramiento default.

※ Variables

  • Las variables deben de tener nombres descriptivos teniendo una mayor prioridad la claridad del nombre que el tamaño del nombre.

  • Entre más uso tenga una variable, más descriptivo deberá ser su nombre, por ejemplo:

    for (const item of myCollection) {
        //do some stuff...
    }

    Es aceptable puesto a que item unicamente existirá dentro del Scope del bucle.

  • Por lo que:

        let usersInLocalDatabase = { 
            /* .. */
        }

    Es aceptable, puesto que es una variable que posiblemente será usada en más de un lugar.

  • Para el uso de variables boolean se recomiendan los sufijos is o has.

  • Se recomienda evitar a toda costa el uso de variables globales, en el caso de usar se debe utilizar el prefijo "global" seguido del nombre en notación Pascal Case ó Upper Camel Case por ejemplo:

    var globalGreetMessage = "Hola mundo!";
    
    function ShowGlobalMessage()
    {
        alert(globalGreetMessage);
    }

Nombramiento

※ Funciones / Métodos

  • Las funciones o métodos deben de tener nombres descriptivos teniendo una mayor prioridad la claridad del nombre que el tamaño del nombre.

  • El nombre de una función debe entenderse como una acción.

  • Al declarar una función o método se utilizará notación Pascal Case ó Upper Camel Case:

    function DoVeryImportantTask()
    {
        //...
    }
  • En el caso de que una función reciba argumentos, estos deberan usarse con notación Camel Case, el mismo caso para las variables locales de una función, por ejemplo:

    function GreetUser(userName)
    {
        let greetMessage = "Hola " + userName;
        //...
    }

※ Idioma

  • Todas las convenciones anteriormente mencionadas debes hacer uso del idioma Inglés sin ninguna excepción.

Formato de código.

※ Identación

  • La identación del código debe de ser de 4 espacios, Visual Studio y Visual Studio Code por defecto tienen esta identación al tabular el texto, una correcta identacion de código dberia verse tal que:
function DoSomeImportantTask(isImportantValue)
{
    if(isImportantValue == true)
    {
        for(let step = 0; step < 5; step++)
        {
            //...
        }
    }
}

※ Cierre y apertura de llaves

  • El cierre y apertura de llaves se puede presentar en dos casos:
    • Para el caso de apertura y cierre de scope se hará un salto de linea antes de abrir una llave.
    • En el caso de apertura y cierre de callbacks ó funciones flechas o funciones anónimas se abrirá con una llave en la misma línea en la que se declara.
function GetAge(edad)
{
    if(age >= 18)
    {
        setInterval(function Grow(){
            age++;
        }, 1000)
    }
}

※ Funciones flecha

  • Unicamente se deberá rodear la lista de parámetros con ( ) si existe más de uno:
Incorrecto
(x) => x + x;
  • Cualquiera de estas opciones es correcta:
Correcto
x => x + x;

(x, y) => x + y;

Estilos

  • Para identificar una clase CSS esta deberá estar escrita en kebab-case, ejemplo: bg-dark, cursor-pointer, text-center.

  • Los identificadores HTML serán nombrados en PascalCase.

  • Los estilos se aplicarán únicamente mediante clases e identificadores, prohibiendo el uso del atributo "style" en los elementos HTML.

Principios de diseño

※ TDD (Test-driven development)

TDD ó desarrollo guiado por pruebas es una técnica de desarrollo de software que se base en primero realizar pruebas y refactoring.

Los pasos a seguir son:

  • Elegir un requisito ó requerimiento: Se elige de una lista el requerimiento que se cree que nos dará mayor conocimiento del problema y que a la vez sea fácilmente implementable.

  • Escribir una prueba: Se comienza escribiendo una prueba para el requisito.

  • Verificar que la prueba falla: Si la prueba no falla es porque el requerimiento ya estaba implementado o porque la prueba es errónea.

  • Escribir la implementación: Escribir el código más sencillo que haga que la prueba funcione.

  • Ejecutar las pruebas: Verificar si todo el conjunto de pruebas funciona correctamente.

  • Eliminación de duplicación: El paso final es la refactorización, que se utilizara principalmente para eliminar código duplicado.

  • Actualización de la lista de requisitos: Se actualiza la lista de requisitos tachando el requisito implementado. Asimismo se agregan requisitos que se hayan visto como necesarios durante este ciclo y se agregan requerimientos de diseño.

※ Encapsulamiento y principios de responsabilidades

Condicionales repetitivas y encapsulamiento lógico

Se pueden llegar a presentar situaciones en donde se tengan condicionales con demaciadas expresiones lógicas y en la mayoría de los casos se requieren de reutilizar las mismas expresiones en otras condicionantes; la regla fundamental es la utilización de funciones anónimas y predicados:

Lógica no refactorizada
const RefreshUI()
{
    boolean hasAudio = musicList.size > 0;
    boolean hasVideo = videoList.size > 0;
    boolean hasKaraokes = karaokeList.size > 0;

    let configurations = getConfigurations();
    if(hasAudio | hasVideo | hasKaraoke)
        ShowDashBoards();

    if((hasAudio | hasVideo | hasKaraoke) && configurations.isAudioEnabled)
        ShowMusicTab();
}
Lógica refactorizada
const RefreshUI()
{
    //Predicados usuarios para lógica generica
    let hasContentPredicate = list => { return list.size > 0; }
    boolean hasAudio = hasContentPredicate(musicList);
    boolean hasVideo = hasContentPredicate(videoList);
    boolean hasKaraokes = hasContentPredicate(karaokeslist);

    //functores y lamdas/funciones anónimas para encapsular expreciones lógicas
    let hasAnyMediaType  = () => { return hasAudio | hasVideo | hasKaraoke; }
    
    let configurations = getConfigurations();  
      
    if(hasAnyMediaType())
       ShowDashBoards();

    if(hasAnyMediaType() && configurations.isAudioEnabled)
        ShowMusicTab();
}

Switch's Legibles:

  • Cuando un bloque switch se vuelve grande, se convierte difícil de leer que es lo que hace especificamente cada caso, para casos como el mencionado se recomendará encapsular las tareas de cada caso en funciones (ya sean anónimas o definidas), es importante identificar que recursos se procesan en esos "bloques" ya que estos principalmente indican el argumento de las posibles funciones a encapsular y termina formalizando la limpieza en donde puedes leer facilmente los casos del switch e identificar que datos son los que se manejan.
Incorrecto
...
switch(value)
{
    case 'AddUserCase':
        let recentlyCreatedUser = {name,pass,email,rights};
        recentlyCreatedUser.name = "example";
        //... some magical stuff inbetwen
        elementList.add(recentlyCreatedUser);
        break;

    case 'DeleteUser':
        someArray.forEach(e =>{
            if(e.name == selectedUser.name)
                someArray.remove(e);
            });
        break;         
}
...
Correcto
...
let AddUser(elementList)
{
    let recentlyCreatedUser = {name,pass,email,rights};
    recentlyCreatedUser.name = "example";
    //... some magical stuff inbetwen
    elementList.add(recentlyCreatedUser);
}

let DeleteUser(someArray,userName)
{
    someArray.forEach(e =>{
        if(e.name == userName)
            someArray.remove(e);
    });
}

...
switch(value)
{
    case 'AddUser':
        AddUser(elementList);
        break;
    case 'DeleteUser':
        DeleteUser(elementList,selectedUser.name);
        break;         
}
...
//IMPORTANT NOTE: THIS CODE IS JUST AN EXAMPLE;VAR DECLARATIONS AND THE STRUCTURE OF THE GENERAL IDEA TO REPRESENT ARE UNDER THE HOOD;

Responsabilidad de una función

  • Cuando una función empieza a tener gran cantidad de código, su número de responsabilidades incrementa, la solución es sencilla: tener en cuenta 2 responsabilidades y en base a estas responsabilidades dividir por pasos las acciones/tareas que hace la función.

Responsabilidades de ejecución

  • Esta es la que agrupa un segmento de funciones:
const Example()
{
    FunctionA();
    FunctionB();
    FunctionC(FunctionD(),data.imageBuffer);
}

Responsabilidades de proceso

  • Esta es la que se encarga de elaborar una acción pertinente.
const FunctionD(inputValue,imageData)
{
    //do some bussines logic
}
  • La responsabilidad de una función encamina al concepto de que tan "abierto/desacoplado" o "cerrado/acoplado" sea el codigo. Para "abrir/desacoplar" código, es importante identificar los recursos que se manejan, ya que estos son la entrada de las funciones que tienen responsabilidades tipo proceso.

※ Bases de datos

Relaciones

  • Una relación es una característica especial de Acceso que hace que podamos trabajar con varias tablas relacionadas a través de un campo en común.

  • El nombre de la foreign key estará designado por "[propiedad] + Id", escrita en camelCase, ejemplo : storeId, storeProductId

Relación de uno a uno:

  • Las relaciones de uno a uno no son las más habituales, y de hecho muchas veces se evitan, pero tenemos ejemplos típicos que se repiten en diferentes aplicaciones: por ejemplo, un proovedor tiene una cuenta.

Relacion uno a uno

Relación de uno a muchos:

  • Una relación uno a muchos es utilizada cuando un modelo puede tener muchos otros modelos relacionados. Por ejemplo, una profesión puede tener un número indeterminado de usuarios asociados a ésta. Dentro del modelo Profession podemos decir que una profesión tiene muchos usuarios:

Relación de uno a muchos

Relación de HasManyThrough :

  • En las relaciones HasManyThrough tenemos una relación a través de otra tabla, desde el modelo de Physician hacia el de Pacient. Estos dos modelos no se encuentran relacionados directamente entre sí, pero sin embargo sí que es posible llegar desde los cursos a los recursos, a través de la tabla de Appointment.

Relación de HasManyThrough

Relación de HasAndBelongsToMany :

  • En las relaciones HasAndBelongsToMany crea una relacion muchos a muchos directa con otro modelo sin necesidad de una tabla intermedia a comparacion de HasManyThrough . Por ejemplo, en una aplicacion con ensamble y partes, donde cada ensamble tiene muchas partes en varios ensambles.

Relación HasAndBelongsToMany

Modelos :

  • El nombré del modelo deberá estar escrito en singular y en PascalCase,

    Correcto Incorrecto
    StoreProduct Storeproduct
    Store Stores
  • El modelo deberá contener sus respectivos ACL's para el control de acceso.

API Path - Principios REST

  • Utilizar los principios REST para manejar acciones CRUD usando métodos HTTP de la siguiente forma:

    • GET /products- Devuelve una lista de products
    • GET /products/12- Devuelve un producto específico
    • POST /products- Crea un nuevo product
    • PUT /products/12- Actualiza el producto #12
    • PATCH /products/12- Actualiza parcialmente el producto #12
    • DELETE /products/12- Elimina el producto #12
  • En cuanto a relaciones se refleja de esta manera:

    • GET /products/12/messages - Devuelve una lista de mensajes para el producto #12
    • GET /products/12/messages/5 - Devuelve el mensaje #5 para el producto #12
    • POST /products/12/messages - Crea un nuevo mensaje en el producto #12
    • PUT /products/12/messages/5 – Actualiza el mensaje #5 para el producto #12
    • PATCH /products/12/messages/5 - Actualiza parcialmente el mensaje #5 para el producto #12
    • DELETE /products/12/messages/5 - Borra el mensaje #5 para el producto #12
  • En caso de agregar un filtro, especificar el parámetro que se estará enviando, por ejemplo:

  • GET /store/sales/12/from/2021-01-31/to/2021-02-03- Devuelve las ventas de la tienda #12 en el intervalo del 31 de enero de 2021 al 03 de febrero del 2021

Referencias: https://api.gov.au/standards/national_api_standards/naming-conventions.html https://restfulapi.net/resource-naming/ https://stoplight.io/blog/rest-api-standards-do-they-even-exist/

Extensión del código

  • El cuerpo de una funcion no deberá de sobrepasar las 20 líneas de código.

  • Un archivo no deberá de sobrepasar las 300 líneas de código.

※ Distribución correcta de los archivos.

  • Las funciones deben estar correctamente distribuidas en sus respectivos archivos, un ejemplo de una mala práctica sería tener la validación / encriptación de los datos del usuario en el mismo archivo en el que se recolectan dichos datos, lo correcto sería tener separados ambos modulos y así:

    • Se reutiliza código.
    • Es más fácil de leer.
    • Es más fácil de reparar.

※ Rutas de navegación.

  • Las rutas de navegación deben estar escritas en kebab-case y nombradas de forma que se pueda identificar el rol y a qué información accederá, por ejemplo: inicio/admin/estudiantes/13/materias En este caso, el administrador está ingresando a la información de las materias del alumno 13.

※ Git.

  • En caso de que exista un Sprint activo se deberá crear una rama sobre develop con el nombre del Sprint, al termino del mismo se hará merge de vuelta a develop.
  • Es necesario realizar un Pull Request hacia la rama de Sprint para ingresar código.

Git Flow,

  • Implementación de ramas "Feature" para la realización de una o varias tareas, tres como máximo y deberán estar relacionadas.
  • La rama "Feature" deberá ser nombrada en base al código de la tarea a realizar, ejemplo: "Feature/BG-236".
  • Implementación de ramas "Bugfix" para realizar correcciones sobre la la rama destino.

Commits

  • Al igual que las convenciones de nombramiento antes mostradas, los commits deben de ser claros y explicitos respecto a los cambios que se hicieron (desarrollo de un feature nuevo, reparación de un bug, refactor de alguna sección, etc), para de esta manera detectar errores más rápido.
  • No usar emojis

Más sobre git: https://project-awesome.org/arslanbilal/git-cheat-sheet Documentación oficial de git: https://git-scm.com/book/en/v2