Boas Práticas & Clean Code

com Golang

Bianca Rosa

lead dev @ stone pagamentos

  • Python
  • Go
  • JS
  • PythOnRio / Pyladies / Rails Girls RJ / WWG RJ

be a clean gopher

go fmt

Não tem nenhum motivo pra não usar a formatação padrão do Go.

Dica: tente automatizar.

Escolha bons nomes

There are only two hard things in Computer Science: cache invalidation and naming things.

Phil Karlton

func parse(s string) string {
    return strings.Replace(s, " ", "", -1)
}
func removeSpaces(s string) string {
    return strings.Replace(s, " ", "", -1)
}
func remove(s string, char string) string {
    return strings.Replace(s, char, "", -1)
}

Effective Go

Nomes de variáveis

Nomes de pacotes

Nomes de receivers

Nome de retorno

Regra do Escoteiro

The Boy Scout Rule

Ao sair da área onde você estava acampado, deixe-a mais limpa do que quando a encontrou.
func (p *Person) EhHoraDeDormir() bool {
    hora := false
    if p.sono == true && !p.ocupada == false {
        hora = true
    } else if time.Now().Hour() > 21 {
        hora = true
    }
    return hora
}
func (p *Person) HoraDeDormir() bool {
    return p.sono == true && !p.ocupada == false || time.Now().Hour() > 21
}
func (p *Person) DevoDormir() bool {
    if p.sono && !p.ocupada {
        return true
    }
    if time.Now().Hour() > 21 {
        return true
    }
    return false
}
func returnUsername(userId int) string {
    if userId == 1 {
        return "Maria"
    } else if userId == 2 {
        return "João"
    } else if userId == 3 {
        return "Joaquim"
    } else if userId == 4 {
        return "Ana Clara"
    }
    return "Não identificado"
}
var users map[int]string
func init() {
    users = map[int]string{
        1: "Maria",
        2: "João",
        3: "Joaquim",
        4: "Ana Clara",
    }
}
func name(userID int) string {
    name, ok := users[userID]
    if ok == false {
        return "Não identificado"
    }
    return name
}

Comentários de código

// valida documentos
for _, document := range clientDocuments {
    go request(rc, url, &document, ro)
}
for _, doc := range documents {
    go validate(channel, &doc)
}

Princípio da Responsabilidade Única (SRP)

type fox struct{}
                            
func (f *fox) Say() {
    fmt.Println("Ring-ding-ding-ding-dingeringeding!")
}
type fox struct{}
                            
func (f *fox) Say() {
    fmt.Println("Ring-ding-ding-ding-dingeringeding!")
}

func (f *fox) Meow() {
    fmt.Println("Meow")
}
type fox struct{}
                
func (f *fox) Say() {
    fmt.Println("Ring-ding-ding-ding-dingeringeding!")
}

type cat struct{}

func (c *cat) Meow() {
    fmt.Println("Meow")
}

DRY

Don't Repeat Yourself

A menos que você esteja criando complexidade desnecessária

SEMPRE cheque seus erros

func converte(s string) int {
    n, _ := strconv.Atoi(s)
    return n
}
func converte(s string) (int, error) {
    n, err := strconv.Atoi(s)
    return n, err
}
func converte(s string) (int) {
    n, err := strconv.Atoi(s)
    if err != nil {
        n = 0
        //log error
    } 
    return n
}

O que não fazer

func hasPermission(roles []Role, statusFlows []StatusFlow, currentStatus, newStatus string) bool {
    for _, role := range roles {
        for _, statusFlow := range statusFlows {
            if strings.ToLower(role.Name) == strings.ToLower(statusFlow.Role) {
                if len(statusFlow.Flows) == 0 {
                    return true
                }
                for _, permission := range statusFlow.Flows {
                    if permission.From == currentStatus {
                        for _, newStatuses := range permission.To {
                            if newStatuses == newStatus {
                                return true
                            }
                        }
                    }
                }
            }
        }
    }
    return false
}

Mas como resolver?

func (u *User) HasPermission(fromStatus, toStatus string) bool {
    for _, role := range u.roles {
        return role.hasPermission(fromStatus, toStatus)
    }
    return false
}
func (r *Role) hasPermission(fromStatus, toStatus string) bool {
    flows := Flows()
    flow := matchedFlow(flows, r.Name)
    if flow != nil {
        return true
    }
    return flow.IsTransitionAllowed(fromStatus, toStatus)
}
func matchedFlow(statusFlows []StatusFlow, name string) *StatusFlow {
    for _, statusFlow := range statusFlows {
        if strings.EqualFold(name, statusFlow.Role) {
            return &statusFlow
        }
    }
    return nil
}
func (f *StatusFlow) IsTransitionAllowed(from, to string) bool {
    if len(f.Flows) == 0 {
        return true
    }
    permission := f.PermissionForTransition(from)
    for _, new := range permission.To {
        if new == to {
            return true
        }
    }
    return false          
}
func (f *StatusFlow) PermissionForTransition(name string) *Permission {
    for _, permission := range f.Flows {
        if permission.From == name {
            return &permission
        }
    }
    return nil
}

twitter: @__biancarosa

slides: biancarosa.com.br/slides/go-clean-code.html

code: github.com/biancarosa/beautiful-go

tks :)