Skip to content

Instantly share code, notes, and snippets.

@eestein
Created June 22, 2020 17:41
Show Gist options
  • Save eestein/4829c9600e2534dd70d49ec3d66864f6 to your computer and use it in GitHub Desktop.
Save eestein/4829c9600e2534dd70d49ec3d66864f6 to your computer and use it in GitHub Desktop.

Guia de Padronização

Assim como em qualquer projeto, ser consistente é extremamente importante. Por isso, criamos uma série de padrões para guiar o desenvolvimento da SDK e ajudar projetos que usam a SDK a manter o mesmo padrão.

Pré requisitos

1 - Variáveis

1.a - Nomenclatura

Comentários são muito úteis, mas pense bem ao nomear suas variáveis. O objetivo é que um comentário não seja necessário para entender o que sua variável faz. Essa abordagem facilita muito a vida de quem faz manutenção no código.

Exemplos válidos:

export class Demo {
    isLoggedIn: boolean;

    private canViewPage: boolean;
}

Exemplos inválidos:

export class Demo {
    /**
     * Indica se o usuário está logado.
     **/
    logged: boolean;

    private canView: boolean; // Indica se o usuário pode ver a página.
}

1.a.i - Variáveis de classe

Todas as variáveis de classe devem ter nomes seguindo o padrão camelCase . As únicas exceções para essa regra são:

  • Variáveis protected podem começar com _ (underline - sublinhado) para diferenciá-las nas implementações.
  • Variáveis private que tenham o mesmo nome de um get/set podem começar com _ (underline - sublinhado). Essa exceção, no entanto, deve ser usada com muita cautela. O ideal seria procurar um nome diferente que representasse a mesma ideia ou adicionar o sufixo local no nome da variável, ex: canViewLocal .
  • Variáveis public que sejam representações locais (acesso ao template - HTML) devem ter o mesmo padrão do import.

Exemplos válidos:

import { DemoTypeEnum } from '../models/enum/demo.type';

export class Auth {
    DemoTypeEnum = DemoTypeEnum;

    isLoggedIn: boolean;

    protected _updatedUserData: UserDataModel;

    private canViewPage: boolean;
}

Exemplos inválidos:

import { DemoTypeEnum } from '../models/enum/demo.type';

export class Auth {
    demoTypeEnum = DemoTypeEnum;

    _isLoggedIn: boolean;

    protected UpdatedUserData: UserDataModel;

    private canview: boolean;
}

1.a.ii - Variáveis de método

Todas as variáveis de método devem ter nomes seguindo o padrão camelCase .

1.a.iii - Variáveis de arrow functions (lambdas)

Todas as variáveis de arrow functions devem ter nomes seguindo o padrão camelCase . Tente usar nomes significativos, mas não longos. Evite usar sempre x ou alguma outra letra padrão.

O exemplo abaixo não seria considerado aceitável:

this.relatedItemsResource.search(request).subscribe(x => {
    // ...
});

Já os próximos, seriam:

this.relatedItemsResource.search(request).subscribe(ri => {
    // ...
});

this.relatedItemsResource.search(request).subscribe(related => {
    // ...
});

this.relatedItemsResource.search(request).subscribe(items => {
    // ...
});

this.relatedItemsResource.search(request).subscribe(relatedItems => {
    // ...
});

Ao nomear variáveis de arrow functions tente ser adaptável à quantidade de linhas dela. Se uma arrow function é curta, um nome como ri pode ser o suficiente, pois o desenvolvedor consegue ver facilmente o que é ri . Quando ela for maior, tente ser mais declarativo como relatedItems , pois no corpo do código o desenvolvedor poderia facilmente se perder e não saber o que representa ri . Todos os exemplos acima são válidos, vai do bom senso do desenvolvedor escolher em qual momento usar cada uma das técnicas apresentadas.

1.b - Ordenação

As variáveis devem ser organizadas da seguinte maneira:

imports para uso no template
recomendação: quebra de linha
com decorators
recomendação: quebra de linha
públicas
públicas readonly
recomendação: quebra de linha
protected
protected readonly
recomendação: quebra de linha
private
private readonly
recomendação: quebra de linha

Exemplo:

import { DemoTypeEnum } from '../models/enum/demo.type';

@Component()
export class AuthComponent {
    DemoTypeEnum = DemoTypeEnum;

    @Input() id: string;
    @Input() type: DemoTypeEnum;

    isLoggedIn: boolean;
    readonly isLoggedInSubject = new Subject<boolean>();

    protected _updatedUserData: UserDataModel;

    private canViewPage: boolean;
}

1.c - Comentários

Siga as orientações abaixo para cada tipo de comentário, mas em todos os casos lembre-se de que devem sempre terminar com ponto final.

1.c.i - Comentários de variáveis public

Comentários de variáveis public devem sempre ser feitos com /** , nunca com // . Eles devem também ter no mínimo 3 linhas:

@Component()
export class DemoComponent {
    /**
     * ID do registro.
     */
    @Input() id: string;
}

Nunca escreva comentários públicos como comentários de uma linha. Os dois exemplos abaixo estão incorretos:

@Component()
export class DemoComponent {
    /** ID do registro. */
    @Input() id: string;
    // Nome da configuração.
    @Input() configName: string;
}

1.c.ii - Comentários de variáveis protected

Seguem o mesmo padrão de Comentários de variáveis public

1.c.iii - Comentários de variáveis private

Comentários de variáveis private devem sempre ser feitos com // , nunca com /** . Eles devem, preferencialmente, ter apenas uma linha e ficar na frente da definição. Caso seja necessário um comentário maior, eles podem ficar em cima da definição e ter mais de uma linha:

export class Demo {
    private id: string; // ID do registro.
    // Indica se o usuário logado tem direito a fazer a carga da página atual.
    private canViewPage: boolean;
    // Índice atual do arquivo de demonstração. 
    // Caso o índice esteja entre -1 e 1, o pai deste item deve ser validado manualmente.
    // Caso contrário, deve ser validado automaticamente.
    private demoIndex: number;
}

Nunca escreva comentários private como comentários multi-linha e nunca deixe um comentário que cabe na frente da definição em cima dela. Os dois exemplos abaixo estão incorretos:

export class Demo {
    // ID do registro.
    private id: string;
    /**
     * Índice atual do arquivo de demonstração. 
     * Caso o índice esteja entre -1 e 1, o pai deste item deve ser validado manualmente.
     * Caso contrário, deve ser validado automaticamente.
     **/
    private demoIndex: number;
}

2 - Métodos

2.a - Nomenclatura

Todas os métodos devem ter nomes seguindo o padrão camelCase . A única exceção para essa regra é se o método for protected . Nesse caso, ele pode começar com _ (underline - sublinhado) para diferenciá-lo nas implementações.

2.b - Ordenação

Os métodos devem ser organizados da seguinte maneira:

construtor
quebra de linha
lifecycle do angular
quebra de linha
com decorators
quebra de linha
públicas
quebra de linha
protected
quebra de linha
private
quebra de linha

Exemplo:

@Component()
export class DemoComponent implements OnInit {
    constructor(
        private myService: MyService
    ) { }

    ngOnInit(): void {
        // ...
    }

    @HostListener('keydown')
    keyDown(): void {
        // ...
    }

    search(): void {
        // ...
    }

    protected _locationSearch(): LocationModel {
        // ...
    }

    private findUser(): UserModel {
        // ...
    }
}

2.c - Comentários

2.c.i - Comentários de métodos public

Comentários de métodos public devem sempre ser feitos com /** , nunca com // . Eles devem também ter no mínimo 3 linhas:

export class Demo {
    /**
     * Gera um slug para o registro atual.
     */
    generateSlug(): string {
        return '';
    }
}

Nunca escreva comentários públicos como comentários de uma linha. Os dois exemplos abaixo estão incorretos:

@Component()
export class DemoComponent {
    /** Gera um slug para o registro atual. */
    generateSlug(): string {
        return '';
    }

    // Obtém o índice atual.
    getCurrentIndex(): number {
        return 0;
    }
}

2.c.ii - Comentários de métodos protected

Seguem o mesmo padrão de "Comentários de métodos public "

2.c.iii - Comentários de métodos private

Seguem o mesmo padrão de "Comentários de métodos public "

2.c.iv - Comentários de lógica

Comentários de lógica devem sempre ser feitos com // , nunca com /** . Exemplo:

export class Demo {
    private openAsDialog(): number {
        // Normalmente, esse processo seria tratado pelo `open` que garante que temos apenas um overlay
        // aberto por vez, mas já que resetamos as variáveis em funções assíncronas, alguns overlays
        // podem passar sem tratamento se o usuário abre e fecha muitas vezes em seguida.
        if (this.dialogRef) {
            this.dialogRef.close();
        }
    }
}

Nunca escreva comentários de lógica com /** . O exemplo abaixo está incorreto:

export class Demo {
    private openAsDialog(): number {
        /**
         * Normalmente, esse processo seria tratado pelo `open` que garante que temos apenas um overlay
         * aberto por vez, mas já que resetamos as variáveis em funções assíncronas, alguns overlays
         * podem passar sem tratamento se o usuário abre e fecha muitas vezes em seguida.
         **/
        if (this.dialogRef) {
            this.dialogRef.close();
        }
    }
}

3 - Classes

3.a - Nomenclatura

Todas as classes devem ter nomes seguindo o padrão PascalCase , também conhecido como TitleCase .

3.b - Comentários

Todos os comentários de classes devem seguir o mesmo padrão de "2.c.i - Comentários de métodos public ".

4 - Interfaces

4.a - Nomenclatura

Todas as interfaces devem ter nomes seguindo o padrão PascalCase , também conhecido como TitleCase e nunca devem começar com I sendo usado como identificador de interface.

Exemplo válido:

export interface OverlayReference { }

export interface IndexConfiguration { }

Exemplos inválidos:

export interface IOverlayReference { }

export interface overlayReference { }

4.b - Comentários

Todos os comentários de interfaces devem seguir o mesmo padrão de "2.c.i - Comentários de métodos public ".

5 - Componentes

5.a - Nomenclatura

Todas os componentes devem ter nomes seguindo o padrão PascalCase , também conhecido como TitleCase e terminar com o sufixo Component .

Exemplos válidos:

export class CalendarComponent { }

export class DatepickerComponent { }

Exemplos inválidos:

export class Calendar { }

export class datepickerComponent { }

export class MyPage { }

export class AddUserView { }

5.b - Comentários

Todos os comentários de componentes devem seguir o mesmo padrão de "2.c.i - Comentários de métodos public ".

6 - Diretivas

6.a - Nomenclatura

Todas as diretivas devem ter nomes seguindo o padrão PascalCase , também conhecido como TitleCase e terminar com o sufixo Directive .

Exemplos válidos:

export class ClaimsDirective { }

export class ScrollableDirective { }

Exemplos inválidos:

export class Claims { }

export class scrollableDirective { }

6.b - Comentários

Todos os comentários de diretivas devem seguir o mesmo padrão de "2.c.i - Comentários de métodos public ".

7 - Enums

7.a - Membros

Todas os membros de um enum devem ter nomes seguindo o padrão PascalCase , também conhecido como TitleCase .

7.b - Nomenclatura

Todas os enums devem ter nomes seguindo o padrão PascalCase , também conhecido como TitleCase .

7.c - Comentários

Todos os comentários de enums e membros de enums devem seguir o mesmo padrão de "2.c.i - Comentários de métodos public ".

8 - Estilo e boas práticas

8.a - Indentação

Todo código deve ter indentação usando espaço em vez de tab e ter tamanho 4 .

8.b - Boas práticas

8.b.i - Métodos de lifecycle do angular

8.b.i.1 - Sempre adicionar interfaces

Sempre adicione as interfaces quando utilizar métodos do lifecycle.

Exemplo válido:

export class DemoComponent implements OnInit, OnDestroy {
    ngOnInit(): void {
        // ...
    }

    ngOnDestroy(): void {
        // ...
    }
}

Exemplos inválidos:

export class DemoComponent {
    ngOnInit(): void {
        // ...
    }

    ngOnDestroy(): void {
        // ...
    }
}

export class DemoComponent implements OnInit {
    ngOnInit(): void {
        // ...
    }

    ngOnDestroy(): void {
        // ...
    }
}
8.b.i.2 - Apenas em seu contexto

Métodos de lifecycle do angular ( ngOnInit , ngOnDestroy , etc) só devem ser utilizados no seu contexto apropriado. Por exemplo o método ngOnInit nunca deveria ser usado numa classe com @Injectable .

8.b.i.3 - Métodos que são conflitantes

Evite usar métodos de lifecycle que são conflitantes entre si. Exemplo: Uma diretiva, normalmente, não deveria usar DoCheck e OnChanges para tratar alterações no mesmo input, já que o ngOnChanges será sempre invocado cada vez que o change detector detectar mudanças.

8.b.i.4 - Não invocar diretamente

Chamadas explícitas aos métodos de lifecycle do angular não devem ser feitas. Essa responsabilidade é do angular. Fazer esse tipo de chamada no código poderia ser confuso. Se você precisa executar uma série de comandos no ngOnInit e em outro momento da sua aplicação, por exemplo, coloque esse código em um método local e invoque esse método no ngOnInit e onde mais precisar.

Exemplo válido:

export class DemoComponent implements OnInit {
    ngOnInit(): void {
        // ...
        this.setupConfig();
    }

    reloadConfig(): void {
        this.setupConfig();
        // ...
    }

    private setupConfig(): void {

    }
}

Exemplo inválido:

export class DemoComponent implements OnInit {
    ngOnInit(): void {
        // ...
    }

    reloadConfig(): void {
        this.ngOnInit();
        // ...
    }
}

8.b.ii - HostListener/HostBinding

Sempre use @HostListener e @HostBinding em vez de usar a tag host nos decorators @Component e @Directive .

Exemplo válido:

@Directive({
    selector: '[appValidator]'
})
export class ValidatorDirective {
    @HostBinding('attr.role') role = 'button';
    
    @HostListener('mouseenter') 
    onMouseEnter(): void {
        // ...
    }
}

Exemplo inválido:

@Directive({
    selector: '[appValidator]',
    host: {
        '[attr.role]': 'role',
        '(mouseenter)': 'onMouseEnter()'
    }
})
export class ValidatorDirective {
    role = 'button';

    onMouseEnter(): void {
        // ...
    }
}

8.b.iii - Tipos desnecessários

Não utilize tipos onde eles não são necessários, como em atribuições de tipos diretos.

Exemplo válido:

export class Demo {
    isOnline = false;
    
    myMethod(): void {
        const a = 10n;
        const b = false;
        const c = new RegExp('c');
        const d = 'string';
    }

    mySecondMethod(a = 3, b = false): void {

    }

    myThirdMethod(a: number, b: boolean): void {
        
    }
}

Exemplo inválido:

export class Demo {
    isOnline: boolean = false;
    
    myMethod(): void {
        const a: bigint = 10n;
        const b: boolean = false;
        const c: RegExp = new RegExp('c');
        const d: string = 'string';
    }

    mySecondMethod(a: number = 5, b: boolean = false): void {
        
    }
}

8.b.iv - Itens não utilizados

Quaisquer itens não utilizados devem ser removidos. Esses itens geram lixo que, com o tempo, vão acumulando e tornando o código cada vez mais difícil de dar manutenção. Por isso, sempre verifique se existem imports, métodos ou variáveis que não estão sendo usados e os remova!

8.b.v - Estilo de chaves

Escolhemos para o projeto o padrão "the one true brace style" (1TBS ou OTBS).

Exemplo válido:

export class Demo {
    myMethod(): void {
        if (isTrue) {
            // ...
        } else {

        }
    }
}

Exemplo inválido:

export class Demo 
{
    myMethod(): void 
    {
        if (isTrue) {
            // ...
        } else 
        {
            
        }

        if (isTrue) {
            // ...
        } 
        else 
        {
            
        }

        if (isTrue) {
            // ...
        } 
        else {
            
        }
    }
}

8.b.vi - Vírgula desnecessária

Apesar de serem consideradas válidas no ECMAScript 5 e até no 3, não recomendamos seu uso. O uso dessas vírgulas desnecessárias pode confundir o desenvolvedor, por isso não usamos.

Exemplo válido:

export class Demo {
    myArray = [
        'Item 1',
        'Item 2'
    ]
}

Exemplo inválido:

export class Demo {
    myArray = [
        'Item 1',
        'Item 2', // <- note a vírgula desnecessária
    ]
}

8.b.vii - Nunca omita as chaves

O JavaScript permite omitir as chaves em um bloco de código que contenha só um comando. Entretanto, muitos consideram essa prática errada, pois podem gerar erros e reduzir a legibilidade do código. Por isso nunca omitimos as chaves.

Exemplo válido:

export class Demo {
    myMethod(): void {
        if (true) {
            return;
        }
    }
}

Exemplo inválido:

export class Demo {
    myMethod(): void {
        if (true) return;
    }
}

8.b.viii - Use sempre os operadores de tipo

É considerada uma boa prática utilizar os operadores de tipo === e !== em vez dos regulares == e != . O motivo é que no JavaScript os operadores == e != fazem uma conversão de tipo que é considerada bem obscura (Abstract Equality Comparison Algorithm). Com isso vários casos "estranhos" são considerados como verdadeiros.

Todos esses exemplos a seguir são considerados true :

[] == false
[] == ![]
3 == '03'

Se isso acontecer numa comparação, aparentemente inocente como a == b , o erro será muito difícil de encontrar. Por esse motivo, sempre use os operadores igualitários de tipo.

8.b.ix - Sempre verifique as propriedades ao utilizar o for in

Utilizar o for in pode retornar propriedades incluídas no prototype. Para evitar que isso aconteça, sempre "proteja" o for in .

Por exemplo, em vez de:

export class Demo {
    myMethod(): void {
        for (key in myObj) {
            const x = myObj[key];
        }
    }
}

Faça:

export class Demo {
    myMethod(): void {
        for (key in myObj) {
            if (myObj.hasOwnProperty(key)) {
                const x = myObj[key];
            }
        }
    }
}

8.b.x - Imports

8.b.x.1 - Ordenação

A ordenação dos imports é muito importante para facilitar a leitura e organização. Nós organizamos os imports em dois grupos e sempre devemos separar esses grupos com uma linha em branco.

Primeiro grupo:

builtin (node)
bibliotecas externas

Segundo grupo:

internos

Exemplo:

import fs from 'fs';
import { DefaultEntityResource } from '@unio/components';

import { DemoModel } from '../models/demo.model';
8.b.x.2 - Nunca use caminhos absolutos

Usar caminhos absolutos nos imports é uma prática ruim, pois o import estará atrelado ao caminho usado na máquina do desenvolvedor.

Exemplos válidos:

import { DemoModel } from '../models/demo.model';
import f from 'library';

Exemplos inválidos:

import { DemoModel } from '/src/models/demo.model';
import f from '/my-drive/path';
8.b.x.3 - Tome cuidado com segmentos desnecessários

Exemplos:

import './../pages/about'; // deveria ser './pages/about'
import '../pages/about'; // deveria ser './pages/about'
import './pages//about'; // deveria ser './pages/about'
import './pages/'; // deveria ser './pages'
import './pages/index'; // deveria ser './pages'
8.b.x.4 - Deixe sempre uma linha após os imports

Após finalizar os imports deixe sempre uma linha em branco antes de começar a escrever o código.

Exemplo válido:

import { DemoModel } from '../models/demo.model';

export class Test {}

Exemplo inválido:

import { DemoModel } from '../models/demo.model';
export class Test {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment