Julio Biason
3 years ago
7 changed files with 167 additions and 40 deletions
@ -1 +1,36 @@
|
||||
# Debuggers São Superestimados |
||||
|
||||
Volta e meia eu escuto alguém reclamando que alguns editores de código são |
||||
ruins porque é difícil usar um debugger dentro deles. Eu diria que essa visão |
||||
está errada. |
||||
|
||||
Mas vamos tirar algo do caminho antes de mais nada: Eu não estou querendo dizer |
||||
que debuggers são ruins e que você não deveria nunca usar um. Debuggers tem o |
||||
seu uso, mas toda vez que eu tentei usar um, era porque havia alguma outra |
||||
coisa faltando. |
||||
|
||||
Quando eu estava usando um framework em Java, eu tive alguns problemas com o |
||||
código que eu havia escrito. Eu esperava que [a aplicação |
||||
explodisse](../programando/deixe-explodir.md) porque eu não coloquei nada para |
||||
lidar com os problemas. O que aconteceu foi que o framework escondeu o erro e |
||||
reiniciou o processamento. Para encontrar o que estava acontecendo, eu tive que |
||||
conectar um debugger e ver o que havia de errado com os dados; se eu não |
||||
fizesse isso, eu não teria a menor ideia de onde estava o problema. |
||||
|
||||
O debugger era necessário aqui? Eu acredito que não. Se o framework mostrasse o |
||||
erro (explodisse, botasse uma textão nos logs, seja lá o que for), eu não |
||||
precisaria usar o debugger. Mas, porque havia coisas faltando, eu fui _forçado_ |
||||
a usar um debugger. |
||||
|
||||
Além disso, a longo prazo, você acaba com problemas em lugares onde você não |
||||
pode conectar um debugger -- por exemplo, no seu ambiente de produção. Você até |
||||
_pode_, mas não _deveria_. Por outro lado, se você estiver [logando |
||||
eventos](../rodando/logs-para-eventos.md), então você veria o que está |
||||
acontecendo, sem um debugger. |
||||
|
||||
De novo, não quero desmerecer debuggers, mas a longo prazo, eles se tornam |
||||
inúteis e acabam apontando para lugares onde o suporte ao redor está faltando. |
||||
|
||||
<!-- |
||||
vim:spelllang=pt: |
||||
--> |
||||
|
@ -1,40 +1,34 @@
|
||||
# Atalhos São Legais, Mas Apenas a Custo Prazo |
||||
|
||||
Várias linguagens/bibliotecas/frameworks tem atalhos para fazer coisas de forma |
||||
mais simples/curta, reduzindo o número de coisas que você precisa fazer para |
||||
algo funcionar. |
||||
Várias linguagens/bibliotecas/frameworks vem com alguma funcionalidade para |
||||
fazer com que você escreva menos código do que normalmente precisaria. |
||||
|
||||
Mas usar esses atalhos sem entender o que eles fazem por baixo dos panos vai |
||||
estragar seu dia no futuro. |
||||
Mas o uso desses atalhos uma hora não vão servir para o que você precisa e aí |
||||
você vai precisar entender o que é que o atalho faz de verdade. |
||||
|
||||
Frameworks e bibliotecas -- e algumas linguagens também -- vem com funções |
||||
auxiliares para a maior parte das coisas que precisam de boilerplate[^1]. Ao |
||||
invés de escrever as mesmas 5 linhas de código, você pode usar apenas uma |
||||
função com um parâmetro; ao invés de usar uma função com 5 parâmetros, você |
||||
pode usar uma função com apenas um parâmetro. Ou você pode usar uma macro em |
||||
cima da sua estrutura/classe e a própria linguagem vai completar os pontos |
||||
faltantes para você. |
||||
Frameworks e bibliotecas -- e até mesmo algumas linguagens -- vêm com "helpers" |
||||
para a maior parte das coisas com boilerplate. Ao invés de digitar 5 linhas de |
||||
código várias vezes, você usa uma função simples; ao invés de escrever a função |
||||
com 5 parâmetros, você ignora alguns dados e usa uma função com apenas um. Ou |
||||
você pode adicionar uma macro de expansão em cima da sua estrutura/classe e os |
||||
pontos faltantes vão ser automaticamente completados. |
||||
|
||||
Não me entenda errado, esses atalhos são muito úteis. |
||||
Não me entenda errado, eu acho atalhos super úteis. |
||||
|
||||
Mas você precisa entender o que essa macro/função está escondendo de você. |
||||
Porque, mais cedo ou mais tarde, você vai encontrar um caso onde o atalho não |
||||
se encaixa perfeitamente e você só precisa mudar um detalhezinho. E aí você vai |
||||
começar a rodar em círculos porque, bom, como é que diabos a macro/função fez |
||||
Mas você precisa entender o que é que a macro/função está escondendo de você. |
||||
Porque mais cedo ou mais tarde, vai aparecer aquele caso em que o atalho não é |
||||
a solução perfeita e você precisa apenas mudar um pequeno detalhe. E aí você |
||||
vai ficar andando em círculos porque, bom, como é que diabos a macro/função faz |
||||
_aquilo_? |
||||
|
||||
Eu já perdi muito tempo tentando fazer coisas com [Spring](http://spring.io/) e |
||||
[Serde](https://serde.rs/) porque eu comecei usando os atalhos e sem entender |
||||
o que eles fazem por baixo dos panos: eu encontrei problemas que o atalho que |
||||
eu estava acostumado a usar não resolvia, o que me obrigou a ir mais fundo na |
||||
documentação. E porque eu pulei alguns passos e fui direto pros atalhos, eu |
||||
tive que voltar e reler tudo de novo para entender _o que_ eu precisava fazer |
||||
diferente do que o atalho fazia para resolver meu problema. |
||||
Eu já me dei mal com [Spring](http://spring.io/) e [Serde](https://serde.rs/') |
||||
porque eu comecei usando os atalhos sem entender o que eles faziam. E quando eu |
||||
precisei resolver um problema que o atalho não conseguia resolver sozinho, eu |
||||
tive que me aprofundar na documentação. E como eu pulei vários passos e fui |
||||
direto pro atalho, eu levei um bom tempo para realmente _entender_ o que é que |
||||
eu precisava fazer de diferente do que o que o atalho faz para resolver meu |
||||
problema. |
||||
|
||||
--- |
||||
|
||||
[^1]: Boilerplate é aquele código que precisa ficar repetindo todas as vezes. |
||||
|
||||
<!-- |
||||
<!-- |
||||
vim:spelllang=pt: |
||||
--> |
||||
|
@ -1 +1,65 @@
|
||||
# Aprenda O Básico de Programação Funcional |
||||
|
||||
Nessa altura do campeonato, você deve ter ouvido falar de como programação |
||||
funcional é legal. Existem vários conceitos nela, mas tenha em mente os |
||||
conceitos mais básicos dela. |
||||
|
||||
Um monte de apresentações sobre programação funcional vêm com palavras |
||||
estranhas como "functors" e "monads". Não que seja prejudicial conhecer o que |
||||
elas querem dizer (aviso: eu ainda não sei). Mas algumas coisas da programação |
||||
funcional são fáceis de entender e usar. |
||||
|
||||
Por exemplo, imutabilidade. Isso quer dizer que os seus dados não podem mudar |
||||
depois de criados. Você tem um registro de um usuário e o usuário mudou de |
||||
senha? Não, não mude o valor do campo de senha, crie um novo registro de |
||||
usuário com a senha atualizada e remova o antigo. Eu sei que isso cria uma |
||||
sequencia de "cria e destrói" que basicamente não fazem sentido (porque você |
||||
deveria alocar memória para um novo usuário, copiar todos os campos com exceção |
||||
de um, definir apenas um campo, e liberar a memória do antigo? Não faz |
||||
sentido!) mas, a longo prazo, isso previne resultados estranhos, principalmente |
||||
depois de você entender e começar a usar threads. |
||||
|
||||
(A ideia é prevenir um estado compartilhado -- memória -- entre partes do seu |
||||
código.) |
||||
|
||||
Outro conceito útil são funções puras. Funções puras são funções que, se |
||||
chamadas com os mesmos parâmetros, sempre retornam o mesmo resultado, não |
||||
importa quantas vezes você as chame. Um exemplo de função não pura é o |
||||
`random()`: cada vez que você chama `random()`, você tem como retorno um valor |
||||
diferente[^1]. Um exemplo de uma função pura seria algo parecido com isso em |
||||
Python: |
||||
|
||||
```python |
||||
def mult(x): |
||||
return x * 4 |
||||
``` |
||||
|
||||
Não importa quantas vezes você chame `mult2)`, sempre terá o 8 como resultado. |
||||
|
||||
Outro exemplo seria nossa senha imutável apresentada acima: Você pode |
||||
facilmente escrever uma função que recebe um registro de usuário e retorna um |
||||
novo registro com a senha alterada. Você pode chamar a função várias vezes e |
||||
ainda ter o mesmo resultado no final. |
||||
|
||||
Funções puras são úteis porque, principalmente, elas são fáceis de serem |
||||
testadas. |
||||
|
||||
Segundo, elas são fáceis de serem encadeadas num [fluxo de |
||||
dados](./fluxo-dedados.md): Como elas não tem estado interno (que é o |
||||
verdadeiro motivo de serem chamadas funções puras), você pode facilmente chamar |
||||
uma depois da outra e não importa quantas vezes você passe valores entre elas, |
||||
elas sempre produzem o mesmo resultado. E como cada função, dado a mesma |
||||
entrada, produz o mesmo resultado, encadeando todas elas _também_ gera o mesmo |
||||
resultado. |
||||
|
||||
Só esses dois conceitos podem fazer com que você tenha mais código (de novo, |
||||
você está criando um novo registro ao invés de simplesmente alterar apenas um |
||||
campo), mas o resultado final é que o seu código ficará mais robusto. |
||||
|
||||
[^1]: Com exceção de Haskell, mas ela requer que você envie uma semente toda |
||||
vez, fazendo com que você tenha valores baseados nessa semente, e assim a |
||||
função ainda é pura. |
||||
|
||||
<!-- |
||||
vim:spelllang=pt: |
||||
--> |
||||
|
@ -1 +1,33 @@
|
||||
# Pense Nos Usuários |
||||
|
||||
Pense em como os dados que você está coletando dos seus usuário será usado -- |
||||
isso é muito importante atualmente, onde "privacidade" é um serviço premium. |
||||
|
||||
Eu já tive uma discussão com um CTO sobre a coleta do número de IMEI na nossa |
||||
aplicação mobile. Basicamente, não tínhamos nenhum motivo para capturar essa |
||||
informação mas, como ele colocou na época, "Nós queremos saber se um usuário |
||||
usa dois telefones, ou se dois usuários usam o mesmo telefone" (e isso não |
||||
seria usado para melhorar o serviço em praticamente nada). Eu levantei o fato |
||||
de que não precisamos dessa informação e que sentia que basicamente estaríamos |
||||
invadindo a privacidade dos nossos usuários. E ainda assim, ele decidiu |
||||
continuar com a coleta. Minha resposta: "Ok, eu vou fazer, mas quero deixar |
||||
anotado que eu não estou feliz com essa solução". |
||||
|
||||
No final, a aplicação foi barrada no store... por estar capturando o IMEI. |
||||
|
||||
Mas há casos e casos. Se você realmente _realmente_ precisar capturar os dados |
||||
do usuário, tenha certeza que estes estão protegidos contra acessos indevidos, |
||||
seja por ações externas (alguém achou uma falha de segurança na sua aplicação) |
||||
ou internal (um funcionário infeliz resolveu levar os dados dos seus cliente |
||||
com ele). |
||||
|
||||
E tenha certeza, _haverá_ uma falha de segurança em algum ponto, é só uma |
||||
questão de tempo. Se você puder, a melhor forma de proteger os dados dos seus |
||||
usuários é nunca capturar dado algum. Quando for encontrada a falha de |
||||
segurança no seu sistema ou quando um colega sair da empresa de forma infeliz, |
||||
não haverão dados para serem expostos ao mundo, de qualquer forma. Não tem como |
||||
ser mais seguro que isso. |
||||
|
||||
<!-- |
||||
vim:spelllang=pt: |
||||
--> |
||||
|
Loading…
Reference in new issue