As dicas a seguir são do livro The Google Resume.
Quem recebe muitos currículos odeia ler um currículo muito longo. Além disso, se há mais de 2 páginas, é provável que você tenha colocado mais coisa que o necessário. Se voce conseguir colocar tudo em somente uma página, ótimo! Senão, duas páginas é o suficiente.
Se você já cortou tudo que pode, experimente outro template de currículo. Provavelmente no Word ou Pages (OSX) é possível encontrar templates de currículo com uma formatação boa para economizar espaço. Outra dica é usar serviços que geram um PDF do seu currículo. Voce consegue fazer isso pelo LinkedIn, que irá gerar um currículo baseado nos dados do seu perfil.
Falando em PDF, SEMPRE envie seu currículo nesse formato. Se a pessoa não conseguir ler seu currículo por que não tem o software necessário para abrir o documento, tenha certeza que ela não irá ler. E qualquer pessoa hoje em dia tem um leitor de PDF, seja qual for o dispositivo que ela esteja usando.
Na maioria esmagadora dos currículos que já vi, as pessoas listam suas responsabilidades e o que elas faziam no dia a dia. Se você colocou qual a posição que você ocupava na empresa, suas responsabilidades e tarefas diárias podem ser inferiridas sem muito esforço. Ao invés disso, exponha o que você fez que causou um impacto positivo na empresa ou projeto que você trabalhava, e qual impacto foi esse. Assim, a pessoa que está lendo seu currículo irá saber que você consegue fazer coisas que importam e que você pode fazer a diferença.
Seu currículo não precisa ser seu histórico profissional completo. Não há necessidade de listar aquele estágio de 10 anos atrás. Até três itens são suficientes para expor suas competências.
Não é necessário também listar experiências profissionais que não são relevantes e relacionadas com a vaga que você está concorrendo. Não faz muito sentido dizer que você tem experiência como garçom em restaurante se você está enviando seu currículo para uma vaga de gerente comercial.
Da mesma forma que sua experiência profissional, seus conhecimentos técnicos e conquistas acadêmicas também devem ser relevantes. Liste somente sua maior conquista acadêmica (ensino superior, especialização, mestrado ou doutorado), qual a instituição, e período de estudo. Não liste qual colégio você frequentou o ensino médio. Isso não importa.
Logo depois das suas informações pessoais, é bom você dizer o que faz de você uma boa escolha para aquela vaga. Uma ou duas frases são suficientes. Diga quais suas características pessoais, profissionais e conhecimentos relevantes você possui para que a pessoa lendo seu currículo possa de cara saber se vale a pena continuar lendo.
Seguindo estas regras: um bom currículo segue esse esquema:
Era isso, espero que tenha te ajudado a escrever um currículo melhor e que você seja chamado(a) para uma entrevista.
]]>Veja a parte 1 aqui
Está rolando uma thread no grupo .NET Architects sobre o Null Object Pattern. O assunto começou com uma dúvida sobre como criar mapeamentos do NHibernate com classes que possuem referencias a um Null Object.
Foram dadas algumas respostas, mas a thread continuou com o assunto e dúvidas surgiram sobre: Null Object é Anti-Pattern? Como usar? Non-Nullable References é melhor?
Vou dar algumas opiniões minhas aqui sobre esse assunto.
Não acho que seja. Mas pode vir a ser. Patterns são coisas que não devem ser abusadas, e é uma linha bem fina sobre quando é necessário e quando não é.
Em muitas estruturas de dados, é comum o uso de um “Null Object”. Por exemplo:
Existe um objeto que representa uma lista vazia. Geralmente é chamado de Nil
ou Empty
.
Existe os nós das árvores. Cada um com seu tipo: Node
geralmente é o tipo base, e daí existe os tipos Leaf
, que representa o último nó de um branch da árvore, e é comum existir o que representa um nó vazio, também chamado de Empty
ou Nil
. Nós Leaf
geralmente usam esse tipo para representar seus sub-nós (que não existem).
Em ambos esses casos, seria melhor ou pior usar um valor null
para representar uma lista vazia, ou os filhos de um nó da árvore que não existem? Minha opinião é: seria muito pior. Tornaria o design da API mais feio, e também deixaria o uso da API mais propenso a erros.
A ideia é muito boa, mas na prática, não é tão simples assim de se implementar. Na thread postaram links para quatro posts falando sobre o assunto:
Parte 1 Parte 2 Parte 3 Parte 4
O Eric Lippert (ex-membro do time do C#) também postou em seu blog sobre o assunto:
É uma pena, pois acho que nunca veremos isso no C#.
Uma linguagem que tem suporte a esse recurso é Kotlin. Kotlin é uma linguagem criada pela Jetbrains, que roda sobre a JVM. Essa é uma das poucas linguagens que eu já vi que se preocupou com isso desde o início.
Já postei aqui sobre o assunto. E, como a thread é do .NET Architects, eu e o Abner (trabalha comigo na Lambda3) criamos um projeto no Github com uma implementação do tipo Option em C#.
Alguns exemplos:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
1 2 3 |
|
1 2 3 |
|
Inclusive com suporte (inicial) a Linq.
1 2 3 4 5 6 7 8 9 10 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
O que acham? “Linq-To-Option” ficou legal?
]]>A aplicação, hospedada no Windows Azure, é feita em ASP.NET MVC, que tem um suporte razoável para execução de requests de maneira assíncrona.
1 2 3 4 5 6 |
|
Então, qualquer chamada que possa demorar, em todo o ciclo de vida do Request, é feito de maneira assíncrona. Chamadas a Banco de Dados (usamos o Azure Table Storage), requisições remotas, entre outras, são todas feitas utilizando o async/await pattern do C#.
Por que isso foi feito? Para não bloquera threads do Thread Pool do IIS. Se toda vez que alguma operação de IO bloqueasse a execução do Request, a thread ficaria bloqueada até o término da operação. E como vamos ter milhares de usuários simultâneos, isso poderia levar à todas as threads disponíveis no Thread Pool do IIS estarem ocupadas, e a aplicação iria parar de responder. Se as operações são executadas de maneira assíncrona, as threads são liberadas assim que a operação inicia, e depois que ela termina, outra thread disponível no Thread Pool é usada para continuar a execução. Assim, nenhuma thread fica bloqueada por muito tempo. Essa é uma das maneiras mais simples (mas não é tão simples assim) de resolver o problema. Node.JS por exemplo, em toda sua API base, só usa operações assíncronas de IO.
Quando conseguimos chegar em um estágio razoavelmente estável da aplicação, começamos a fazer testes de carga, rodamos alguns cenários: com algumas centenas de usuários simultâneos e poucas instâncias (umas 2 ou 3) executando a aplicação. Os resultado foram bons. Conseguimos responder 200 requests simultâneos tranquilamente, com um tempo de resposta razoável, na média de 1-2 segundos.
Mas, quando subimos para 1000 usuários simultâneos e 10 instâncias, os resultados foram muito ruins. A aplicação começou a parar de responder, e tinhamos uma média de tempo de resposta de 10 segundos! Inaceitável.
Por que? Tinhamos feito tudo bonito, async e await para todos os lados. O uso de CPU e de memória nas VMs estava baixo. O que estava acontecendo?
Configuramos o New Relic para monitorar a aplicação e o que vimos é que tinha um método do ASP.NET que estava demorando muito para responder: System.Web.HttpApplication.BeginRequest
. Uma rápida pesquisa no Google nos levou a algumas possibilidades, e uma delas, era de que esse método estava bloqueando enquando o ASP.NET esperava threads serem liberadas para processar o request.
Durante a caçada ao problema, percebi uma coisa estranha. Tinhamos um mecanismo de gravação de logs na aplicação. Como eram gerados uma quantidade razoável de logs, as vezes durante um único request, eles eram escritos de maneira assíncrona também. Dando uma olhada na implementação de um TraceListener customizado nosso, vi que no método Flush, esperavamos todas as operações de escrita no log terminarem:
1 2 3 4 5 |
|
Até aí tudo bem. Como os métodos de escrita de log seguiam o esquema “fire-and-forget”, o método Flush
não fazia muita diferença. Só se em alguma parte do sistema fosse necessário esperar a escrita de operações de Log. O que foi surpresa para mim, é que na configuração de logs da aplicação, a propriedade autoflush
estava ligada!
1 2 3 4 |
|
Ou seja, cada vez que era executado um Trace.Write("...")
na aplicação, a execução bloqueava esperando a escrita do log terminar. Como isso ocorria com uma certa frequencia, basicamente em todos os requests a execução bloqueava por um determinado período de tempo. Desligada a opção autoflush
, feito o deploy novamente, e após a execução dos mesmos testes de carga, o tempo de resposta ficou na média de 1-2 segundos e a aplicação estava respondendo 6 vezes mais requisições durante a execução inteira do teste de carga.
Antes da mudança de configuração, tinhamos um tempo médio de resposta a cada requisição de 10 segundos, e durante o teste de carga (10 minutos), eram executados entre 15 e 20 mil cenários (todo um fluxo de teste com várias interações no sistema). Após desligar o autoflush
, o tempo médio de resposta caiu para 1-2 segundos, e era executado, nos mesmos 10 minutos do teste, entre 80 e 90 mil cenarios. Todas as vezes usamos 10 instâncias de VMs do tamanho medium do Windows Azure. Os agentes de execução dos testes de carga rodavam localmente em um servidor no nosso escritório em SP.
Vimos na prática que bloquear a execução de threads do web server por muito tempo, em um ambiente com uma enorme quantidade de usuários concorrentes vai ser um grande golpe na performance do sistema. É muito bom ver que o pessoal que desenvolve frameworks já está ligado nisso e que isso hoje já não é tão dificil de resolver. Já vi soluções muito boas em frameworks conhecidos nas plataformas .NET (ASP.NET MVC), Node (Express), Scala (Play+Akka, Finagle+Finatra). Esses são alguns que eu conheço. Não sei dizer como andam as coisas no mundo Rails e Django por exemplo.
Claro que nosso cenário ainda não chega perto do problema do Twitter, Facebook, Amazon, etc.. Esses casos são bem mais complexos. Mas, no nosso cenário, bastando a execução assíncrona de IO, conseguimos escalar bem a aplicação para uma quantidade na casa dos milhares de usuários simultâneos.
]]>NullPointerException
. Tratar objetos que podem conter um valor, ou não, é chato, e muito propenso à erros. Quem nunca esqueceu de fazer um check contra valores nulos?
1
|
|
ou ainda
1 2 |
|
Seria bom se houvesse alguma maneira de evitar esse tipo de problema de Exceptions serem lançadas por que o desenvolvedor esqueceu de tratar um valor que possivelmente veio nulo.
Desde que comecei a me aventurar pelo mundo de linguagens funcionais, percebi que lá, eu não me preocupava com isso. E isso se devia ao fato de que eu usava um tipo de dados bem interessante: o tipo Option
.
1 2 3 4 |
|
Como voce pode ver, o tipo Option simplesmente encapsula um valor possivelmente nulo. E existem os tipos concretos, chamados Some
e None
. Abaixo a declaração de ambos.
1 2 3 4 5 6 7 8 |
|
E também temos um objeto Option com o método apply que cria um valor do tipo Option com base no valor passado como parâmetro:
1 2 3 |
|
Assim, podemos criar valores do tipo Option
da seguinte maneira:
1 2 |
|
1 2 |
|
Como voces podem ver, o encapsulamento do valor é feito, mas ainda sim, podemos cair em uma Exception no caso de tentarmos acessar o value
do Option. Quer dizer então que o Option não serve para nada? Não é bem assim.
O poder verdadeiro do Option está em suas higher-order functions map
e flatMap
. Na definição da trait Option
, temos a declaração dos métodos:
1 2 3 4 5 |
|
Qual a útilidade desses métodos? Simples:
Option
, se houver um;Option
Como assim?
1 2 3 4 |
|
Como vimos, com os métodos flatMap
e map
podemos acessar o valor contido dentro do Option
, também é possível transformar o valor em um outro valor do tipo Option
. No exemplo acima, criamos um Some(10)
e depois o transformamos, multiplicando o valor por 2. Assim, obtemos um Some(20)
. Logo em seguida, usamos o método map
para chamarmos o método println
que imprime o valor na tela. E o legal é que, caso em alguma chamada a map
ou flatMap
, apareça um valor null
, o resultado vai ser um None
. E qualquer chamada a uma dessas funções sobre um valor None
, nada irá acontecer, e assim, nenhuma NullReferenceException
será lançada. Legal não é?
Também é possível obter o valor contido dentro do Option
através de Pattern Matching:
1 2 3 4 5 6 |
|
1 2 3 4 5 6 |
|
Nos exemplos acima, em um caso, o valor é um Some(10)
, então ele cai no primeiro caso, e imprime o valor 10. No segundo caso, como é um None
, ele cai no segundo caso, e imprime “Vazio”.
Um exemplo mais “mundo real” seria:
1 2 3 |
|
No exemplo acima, temos uma lista de pessoas pessoas
, e chamamos o método find
para tentarmos achar dentro dessa lista, uma pessoa chamada “João”. Como o João pode não existir dentro da lista, ele retorna um Option[Pessoa]
. Daí, pegamos o salário do possível valor do joao
com a funcão map
. No final, imprimimos o valor do salário. Mas, como o resultado final pode ser um None
, chamamos mais uma função interessante chamada getOrElse
. O que essa função faz é simples.
1 2 3 4 5 |
|
Como podemos ver, ela faz um Pattern Matching sobre o próprio valor, e caso ele seja um Some[T]
, retornamos o próprio valor, caso seja um None
, retornamos o valor default passado como parâmetro.
Então, no exemplo do João acima, caso o João exista na lista, o seu salário será impresso na tela, caso não, será impresso o valor 0.0.
O legal do Option
é que através da definição do seu tipo, e de suas higher-order functions, torna-se difícil uma NullReferenceException
ser lançada. Toda vez que alguma função possa retornar um valor nulo, basta retornar um valor do tipo Option[T]
, ao invés de simplesmente um valor do tipo T
. A partir daí, é só usar as funções map
e flatMap
, que elas se encarregam de tratar os casos onde o valor for nulo, propagando o None
por toda a cadeia de chamadas.
Esse já deve ser meu terceiro ou quarto primeiro post em um blog que inicio, depois de tentativas fracassadas em escrever sobre algum assunto, que na época julgava interessante. E, já passei um tempo matutando sobre o que escrever no primeiro post dessa nova empreitada. É bem difícil decidir o assunto do primeiro post, como escrevê-lo e qual mensagem passar. Então, vai lá minha nova tentativa.
Demorei muito para finalmente conseguir passar do primeiro parágrafo deste blog. Entre dúvidas de assuntos e uma certa resistência em digitar as primeiras palavras, acabava desistindo. Até que um dia, me deparei com um livro que me fez acordar para alguns problemas que estava enfrentando.
Neste livro, o autor fala sobre alguns assuntos, e um dos principais temas é sobre Resistência, e como ela acaba nos bloqueando de fazer o que a gente quer ou precisa fazer. Abaixo segue dois trechos traduzidos do livro:
Resistência infalivelmente vai apontar para o Norte – isso significa aquela ação ou tarefa que ela quer evitar que façamos. Podemos usar isso. Podemos usá-la como uma bússola. Nós podemos navegar por Resistência, deixando-a nos guiar para aquela ação ou tarefa que devemos seguir antes de todas as outras.
Regra de ouro: Quanto mais importante uma tarefa ou ação for para a evolução da nossa alma, mais Resistência iremos sentir em perseguí-la.
Então, como voces viram pelo primeiro parágrafo deste post, eu me identifiquei bastante por essa idéia. É algo que eu já senti algumas vezes na vida quando queria começar a fazer algo diferente.
E este post é o resultado de uma tentativa de mudança de hábito após começar a ler esse livro. Aqui voce provavelmente vai encontrar coisas sobre diversos assuntos, desde tecnologia, alguns ocasionais reviews de livros que li, opiniões minhas sobre algum assunto, entre outras coisas. Espero ter sucesso! Desejem-me sorte.
Abraços
Breno
]]>