Rules to maintain sanity developing PHP/Laravel with others

  • Be consistent when placing business logic. If you picked controller stick to it!
  • Be careful with {!! variable !!}, I mean REALLY careful. XSS is so easy to exploit when found
  • Stop bringing data from all places. If using blade template choose to receive data from controller or vue component, both can be confusing
  • Migrations, use migrations! Your big mess called database.sql will not be maintainable over long run
  • If your methods are 900+ LOC something MUST be wrong. Tests? Yes be elitist
  • Vue, Angular, jquery and vanilla js at same application will bring problems even if your team members are at guru skill level
  • Try to stick to default community conventions, stackoverflow is more helpful this way 😉
Anúncios

Practical tests and job interviews

Last year I was dedicated to search good opportunities on-line and almost every position I cared started their hiring process with algorithm testing. Everything was fine until these tests started getting boring, most of them are 30 minutes vs 10 challenges format. If you try to write the amazing code time will get you, when I do awesome run my top solved are seven challenges. At the beginning I got depressed thinking if I was a proper dev.

Below is my algorithm for challenge of writing mean distance software:

package main

import (
	"fmt"
	"math"
)

type point struct {
	x, y float64
}

func distance(a, b point) float64 {
	return math.Sqrt(math.Pow((b.x-a.x), 2.0) + math.Pow((b.y-a.y), 2.0))
}

func mean(n ...float64) float64 {
	sum := 0.0
	for _, v := range n {
		sum += v
	}
	return sum / float64(len(n))
}

func meanDist(points ...point) float64 {
	dsts := []float64{}
	for i := 0; i < len(points); i++ {
		for j := i + 1; j < len(points); j++ {
			dsts = append(dsts, distance(points[i], points[j]))
		}
	}
	return mean(dsts...)
}

func main() {
	points := []point{
		{0.0, 10.0},
		{0.0, 20.0},
		{0.0, 30.0},
	}

	fmt.Println(meanDist(points...))
}

I’m very confident which this kinda of challenge because in my master we used many types of distances and mean is the most used statistic. The reply I got “very good”, you can read this as you’re over 80% of others candidates. How on this damned world of people with formal programming classes I can write good enough code to be called to interviews? Yes, you can be confident of your skills, what your learned cannot be taken from you.

Tratem seus erros!

Ando lendo muitas postagens com exemplos de códigos escritos em Go e uma coisa que me incomoda bastante em exemplos do uso do pacote net/http é a falta de checagem de erros.

Geralmente os exemplos estão bem estruturados mas sempre falham em um ponto. Não verificam o resultado de erros da função http.ListenAndServe()!

package main

import (
	"fmt"
	"html"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
	})
	http.ListenAndServe(":8080", nil)
}

A última linha deveria conter pelo menos um log.Fatal() para que exista a possibilidade de saber o que aconteceu com a sua aplicação. Imagine que a porta 8080 já foi ocupada por outro processo, sua aplicação vai encerrar imediatamente e não vai emitir nenhum aviso sobre o problema.

package main

import (
	"fmt"
	"html"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
	})
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Você também deveria usar uma versão customizada do http.Server{} em vez de usar o default da biblioteca mas isso vai ser assunto pra outras postagem.

Limitando API: exemplo em Go

Hoje em dia é super comum o uso de API para alimentar aplicações que as vezes esquecemos que elas possuem algumas deficiências. Se o seu site/aplicação possui um backend em forma de API sabe que geralmente ela é publica e é suscetível vários tipos de ataques. Um dos mais comuns é o abuso da utilização da API e consequentemente criando carga no banco de dados podendo deixar toda a infraestrutura lenta.

A solução mais comum é criar um limitador global de requisições. Primeiro vamos criar uma estrutura para armazenar nossas informações.

type limit struct {
    max int
    interval time.Duration
    counter int
    mux sync.Mutex
}
  • max: número máximo de requisições
  • interval: intervalo até ocorrer o resete do número de requisições
  • counter: número atual de requisições
  • mux: forma idiomática de como indicar que todos os elementos dessa estrutura vão sofrer lock/unlock

A lógica é permitir todas as requisições e ir incrementando nosso counter. Periodicamente vamos resetar o contador pelo intervalo definido interval.

Vamos definir alguns métodos para nossa estrutura de controle.

func (l *limit) pass() bool {
    l.mux.Lock()
    defer l.mux.Unlock()
    if l.counter >= l.max {
        return false
    }
    l.counter++
    return true
}

func (l *limit) start() {
    ticker := time.NewTicker(l.interval)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            l.mux.Lock()
            l.counter = 0
            l.mux.Unlock()
        }
    }
}

func (l *limit) toLimit(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !l.pass() {
            http.Error(w, "Rate limiting", http.StatusTooManyRequests)
            return
        }
        next.ServeHTTP(w, r)
    })
}
  • pass: informa se é permitido a execução da requisição atual
  • start: é uma função que vamos chamar em paralelo com a execução da nossa aplicação que faz o resete do nosso contador periodicamente
  • toLimit: nosso middleware que efetivamente realiza a passagem ou rejeição da requisição

A utilização do código é sem rodeios. Crie uma instância da estrutura limit definindo os valores de max e interval, inicie com start e finalmente passe o middleware para a sua cadeia de middlewares.

...
limits := &limit{
        max: 100,
        interval: 10 * time.Second,
    }

go limits.start()
...
E no seu router/dispacher preferido defina o uso do middleware.
...
r := mux.NewRouter()
r.Use(limits.toLimit, logger, auth)
...
Com menos de 50 linhas você pode evitar que seu banco de dados saia de produção, claro que só fazemos isso se essa preocupação existe.
Código completo aqui!
Como diz minha filha tchau, beijos.

 

Trabalho

Nessas três semanas andei escrevendo uma aplicação que imita o saudoso stackoverflow.com. Estou impressionado como o desenvolvimento em frontend parece avançar devagar. Muita coisa melhorou como por exemplo os frameworks de CSS e o Node.js cresceu absurdamente no número de pacotes.

Eu ainda devo ser muito ruim pois a diferença no número de linhas do back para o front foi de apenas 50%. Como que o back implementado seguindo as recomendações da OWASP e com JWT, argon2 implementados manualmente resulta em apenas 1.5x o tamanho do front? A aplicação ainda tem suporte a PostgreSQL e MongoDB. Um total de 883 dependências foram instaladas pelo NPM. Isso me assusta muito! Mesmo a aplicação usando o conceito Single Page Application (SPA).

Tive vários gotchas também pela minha inexperiência com ECMA. Como diabos o ECMA me implementa suporte a inteiros de 64 bits com apenas 53 bits de precisão? Foi tão frustante ter que mudar a lógica de aquisição de IDs pq o ECMA começa a truncar os números depois da precisão segura. Acho muito estranho esse padrão de implementação de grandes inteiros como ponto flutuante. Essa então que os desavisados acabam perdendo precisão é uma maravilha. Mais sobre o assunto nesse aqui.

Outra coisa que me incomodou bastante no Vue.js foi a comunicação entre componentes pai e filho. De pai para filho é possível usar props mas de filho para pai se usa emit. Eu entendo a justificativa de proteção dos dados do pai mas isso acaba virando bem tedioso depois de um tempo.

Outra coisa maravilhosa do Go que utilizei essa semana foi o suporte nativo a benchmarks. Havia uma duvida se era mais performativo realizar um copy de um slice ou criar um novo método que seria chamado em um loop. O copy era mais rápido e realiza duas alocações a menos do que a solução que meu colega de trabalho sugeriu.

Abaixo o Bootstrap e viva o/a/x Bulma. Até a próxima.

Os Dados do Museu Nacional

Provavelmente todo mundo já ficou sabendo do incêndio do Museu Nacional. Infelizmente lá se foi mais uma vez grande parte do conhecimento do país e não tem volta.

Nessas horas tristes que vale lembrar mais uma vez das iniciativas de dados sobre biodiversidade. Pelo menos algo foi salvo dessa tragédia.

O explorador do SiBBr possui 166.147 registros de ocorrência e o IPT (mais atualizado) possui 380.542. Além dos artigos e a formação pessoal são esses números que sobraram. E tomem cuidado pq essas iniciativas também estão abandonadas como o Museu e pode ocorrer um “apagão” de alguns servidores…

Desenvolvimento e família

No momento estou com um tempo livre e pouco dinheiro e isso permite realizar coisas prazerosas na vida. Tudo realmente tem seu lado positivo!

Para não ficar com a mente ociosa acabei lançando o checkports.online, uma web app simples para checar se a porta de algum computador na internet está disponível para conexões. Foi um ótimo exercício implementar uma worker pool em Go e usar Vue e reCaptcha no front. Vamos ver se pelo menos ajuda no pagamento da própria hospedagem.

Na parte de família brinquei de casinha com minha filha por algumas horas. Batom, cabelo sendo escovado e por ai vai. Muito gratificante ver o amor tão puro de uma criança.

marianinhos