quarta-feira, 24 de fevereiro de 2010

Complexidade

Relendo o excelente artigo Anatomy of a Crash, fiquei impressionado de novo não só com a rapidez com que uma batida é tratada, mas também com a complexidade do sistema todo. O texto narra a reconstrução de uma batida simulada como parte de um estudo em segurança de veículos:

0 milliseconds - An external object touches the driver's door.

1 ms - The car's door pressure sensor detects a pressure wave.

2 ms - An acceleration sensor in the C-pillar behind the rear door also detects a crash event.

2.5 ms - A sensor in the car's centre detects crash vibrations.

5 ms - Car's crash computer checks for insignificant crash events, such as a shopping trolley impact or incidental contact. It is still working out the severity of the crash. Door intrusion structure begins to absorb energy.

6.5 ms - Door pressure sensor registers peak pressures.

7 ms - Crash computer confirms a serious crash and calculates its actions.

8 ms - Computer sends a "fire" signal to side airbag. Meanwhile, B-pillar begins to crumple inwards and energy begins to transfer into cross-car load path beneath the occupant.

8.5 ms - Side airbag system fires.

15 ms - Roof begins to absorb part of the impact. Airbag bursts through seat foam and begins to fill.All over in the blink of an eye

17 ms - Cross-car load path and structure under rear seat reach maximum load.

Airbag covers occupant's chest and begins to push the shoulder away from impact zone.

20 ms - Door and B-pillar begin to push on front seat. Airbag begins to push occupant's chest away from the impact.

27 ms - Impact velocity has halved from 50 km/h to 23.5 km/h. A "pusher block" in the seat moves occupant's pelvis away from impact zone. Airbag starts controlled deflation.

30 ms - The Falcon has absorbed all crash energy. Airbag remains in place. For a brief moment, occupant experiences maximum force equal to 12 times the force of gravity.

45 ms - Occupant and airbag move together with deforming side structure.

50 ms - Crash computer unlocks car's doors. Passenger safety cell begins to rebound, pushing doors away from occupant.

70 ms - Airbag continues to deflate. Occupant moves back towards middle of car.

Engineers classify crash as "complete".

150-300 ms - Occupant becomes aware of collision.

Imagine um site moderno, que tem código rodando no browser e código no servidor (números inventados de cabeça mas não totalmente irreais):

0 ms - usuário digita o endereço de uma página do seu site

1 ms - o código do cliente determina o que fazer com a URL e dispara uma chamada ao servidor solicitando dados

16 ms - front-end do site recebe o pedido, determina que parte do site deve ser executada a partir da URL e dos dados da requisição HTTP

17 ms - serviço responsável por tratar a parte do site que o usuário quer recebe o pedido, decodifica parâmetros e dispara chamadas remotas a dezenas de back-ends

20 ms - cada back-end envolvido no serviço recebe uma requisição e acessa alguma forma de banco de dados para buscar as informações necessárias

25 ms - serviço recebe os dados de volta e calcula resposta a ser enviada de volta para o cliente, empacota tudo em algum objeto e retorna

40 ms - browser recebe resposta HTTP, chama callback do cliente para tratar os dados

42 ms - cliente preenche interface com os dados recebidos, montando a árvore DOM para a próxima interação

45-150ms - enquanto imagens carregam, usuário começa a ler o texto que já está disponível, possivelmente tomando a próxima ação (se o usuário está no IE6, a primeira requisição provavelmente só foi disparada pro servidor agora...)

Essa descrição é propositadamente superficial. Alguns dos detalhes omitidos são autenticação, serialização e desserialização de pedidos e respostas, caching em vários níveis do sistema, sharding e balanceamento de carga, logging, validação de entrada no cliente e no servidor, o modelo ou esquema de armazenamento de dados para acesso eficiente, bordinhas redondas e degradês em todos os browsers, monitoramento, XSS, XSRF, cookies, injeção de dependências, internacionalização, e as pilhas do sistema operacional e de rede de todas as máquinas envolvidas no processo. Além disso, cada front-end e back-end envolvido tem suas próprias camadas de complexidade, ou abstração. Ah, e tem um bug aqui e outro ali.

A grande vantagem pra quem trabalha na engenharia de software sobre outros tipos de engenharia em termos de confiança no sistema é que se tem a oportunidade de testar exatamente o código que vai rodar em produção durante o desenvolvimento. Nós podemos inflar o airbag que vai ser instalado no próximo carro da linha de produção, acender cada fósforo que vai entrar na próxima caixinha. O perigo de confiar totalmente nisso é que a soma da compreensão das partes não é a compreensão do todo.

E quando tudo falhar em produção, teremos aquela grande imagem cheia de detalhes para depurar; analisando várias partes descobriremos que aquele airbag que nós sabíamos que funcionava não foi inflado. Onde no meio de tantas interações ficou faltando o disparo do airbag praquele usuário que teve uma batida a um ângulo de 23ᵒ, trafegando a 45km/h com 2 passageiros numa tarde de sábado de um ano bissexto?

Só não se sente intimidado por um site moderno, ou pelos bugs que podem aparecer nele, quem não sabe como ele funciona.

terça-feira, 2 de fevereiro de 2010

20 lições importantes

Recentemente a Guta me disse que eu estava muito ranzinza nesse blog e que faltava humildade. Reclamei do Joel, reclamei do artigo sobre manutenção, disse que meu artigo era melhor que o do Stroustrup... ela tem razão e não quero que esse blog seja um lugar para se falar mal dos outros. Assim, começo o ano de 2010 falando muito bem de um artigo que li durante as férias: Top 20 programming lessons I've learned in 20 years. O autor é Jonathan Danylko. Eu concordo com tudo!

Segue abaixo então minha tradução livre do artigo. Aliás, livre até demais: eu mantive as 20 lições intactas, mas os comentários são meus.
  1. Decida quanto tempo você quer gastar com um problema antes de começar. Ao invés de ficar pelejando por uma semana com um problema que deveria levar um dia pra resolver, como eu fiz na semana passada, decida antecipadamente quanto tempo você vai dedicar a ele. Pode ser uma semana, um dia, uma hora, 30 minutos, não importa. Se você não conseguir resolver o problema no tempo em que você se permitiu trabalhar nele, procure ajuda ou repense suas prioridades ao invés de tentar ser o super-programador.
  2. Uma linguagem é uma linguagem é uma linguagem. Um sábio professor uma vez me disse que o problema que o seu programador mais júnior está tendo com a namorada tem mais impacto no sucesso do seu projeto do que a linguagem de programação que você escolher. Ele tinha razão. Escolha uma linguagem que te dê conforto, e pronto.
  3. Não exagere nos design patterns. Pense duas vezes antes de usá-las - se houver uma solução mais simples, use-a. 
  4. Faça backups do seu trabalho diariamente.
  5. Você não é o melhor programador. Conforme-se com isso. Sempre tem alguém melhor que você.  Aprenda com eles ao invés de tentar ser melhor.
  6. Aprenda a aprender mais. Se você não está gastando pelo menos 50% do seu tempo aprendendo, sua produtividade está piorando. Sim, parece um paradoxo, mas não é.
  7. Mudanças são constantes. O que você sabe hoje não vai ser tão importante amanhã. Diversifique seus conhecimentos. 
  8. Apoie os menos experientes. Ajudar os programadores da sua equipe a se tornarem melhores é o melhor investimento a longo prazo que você pode fazer.
  9. Simplifique seu algoritmo. Quando terminar, volte atrás e veja o que você pode fazer para tornar seu código mais simples e fácil de ler. O futuro agradece.
  10. Documente seu código. De novo, o futuro agradece. Aliás, quem provavelmente mais agradecerá será você mesmo.
  11. Teste, teste, teste.
  12. Celebre cada sucesso. Não espere uma data longínqua para se dar os parabéns. Programar é cansativo. Comemore cada passo a frente e cada bug removido, da maneira que preferir. Só não comemore comendo chocolate, como eu faço.
  13. Faça revisões de código. No Google cada linha de código que é escrita é revisada por pelo menos um outro programador. Nenhuma outra atividade é tão importante para se garantir a qualidade dos sistemas. Mesmo que você não chegue a esse extremo, peça a seus colegas que revisem as partes importantes e ofereça-se para fazer o mesmo.
  14. Estude seu código antigo. Leia código que você escreveu quando ainda não era tudo isso e maravilhe-se com o quanto você aprendeu desde então.
  15. Tenha senso de humor. Ha ha.
  16. Evite trabalhar com programadores possessivos ou prepotentes. O possessivo é aquele que não permite que ninguém altere ou critique seu código; ele pode arruinar seu projeto. O prepotente é aquele que tem sempre a última palavra; ele vai prejudicar sua auto-estima.
  17. Nenhum projeto é simples. Nenhum. Não existe nada de qualidade em computação que possa ser feito "rapidinho, em dois dias".
  18. Cuidado com o excesso de confiança. Enquanto não estiver funcionando e aprovado, nunca está 90% completo. Os 10% que faltam em geral levam o mesmo tempo que você gastou com os 90%.
  19. Software nunca está pronto. Leia o post Persistir ou jogar tudo fora? 
  20. Paciência é uma virtude. Saiba esperar a hora certa de implementar; antes tente entender seu problema. Quando as coisas derem errado, procure entender por que ao invés de sair tentando qualquer coisa.