Blog

  • Agilidade em tempos de IA

    Agilidade em tempos de IA

    Eu me considero “agilista” desde os tempos do RUP. Sim, aquele RUP: iterativo no papel, mas tão cerimonioso que precisava de um documento para explicar os outros documentos. Foi a minha porta de entrada para a ideia de que software se constrói em ciclos, e de lá para cá acompanhei o Manifesto Ágil virar Scrum, Scrum virar “Scrum, mas…”, e Kanban virar a coisa que a gente faz quando cansa de Scrum.

    Agora tem um agente de IA sentado na minha mesa escrevendo código mais rápido do que eu consigo ler. E a pergunta que não sai da minha cabeça é: os rituais que eu defendi durante vinte e tantos anos ainda fazem sentido?

    Para responder isso eu precisei de um modelo mental e de uma volta no tempo. Tenha paciência comigo.

    Quatro pontas de um diamante

    O Harold Leavitt, lá em 1964, descreveu uma organização como um diamante com quatro pontas: estrutura, tarefas, pessoas e tecnologia. O detalhe importante do modelo não são as pontas, são as setas entre elas. Está tudo interligado: mexeu numa, as outras se mexem junto, querendo ou não.

    Em desenvolvimento de software a gente costuma simplificar isso para um triângulo de pessoas, processos e tecnologia. É nesse triângulo que a nossa conversa acontece. A IA mexeu, e muito, na ponta da tecnologia. Não dá para fingir que as outras duas vão ficar paradas no lugar. E como as pessoas continuam sendo as mesmas (por enquanto), a ponta que mais vai sentir o tranco é a dos processos.

    E processo, em software, sempre foi uma história sobre uma coisa só.

    Sempre foi sobre feedback

    Em 1959 a gente (a gente = programador e não eu, que nem era nascido 😛) programava em COBOL no estilo code and fix, sem método nenhum. Deu tão certo (#sqn) que em 1968 a OTAN precisou convocar uma conferência para inventar o termo “engenharia de software” e tentar colocar ordem na bagunça. Veio o Waterfall em 1970, o Modelo Espiral do Boehm em 1988, o RUP em 1999 e, em 2001, o Manifesto Ágil como reação a tudo isso.

    Se você olhar essa linha do tempo de longe, percebe que ela conta uma história só: a briga contra o feedback tardio. O Waterfall congelava requisitos no início e só descobria que estava tudo errado na homologação, quando mudar ou corrigir alguma coisa custava uma fortuna. O Boehm desenhou uma curva mostrando que o custo de mudar cresce exponencialmente conforme o projeto avança: uma correção barata na fase de requisitos vira um pesadelo em produção. O Ágil foi, essencialmente, a decisão de encurtar os ciclos para pegar os erros enquanto ainda estavam na parte baixa dessa curva.

    Guardem essa curva. Ela vai voltar.

    O gargalo mudou de lugar

    Durante toda a história que eu contei acima, escrever código era a parte lenta. Revisar era rápido, testar era relativamente rápido, discutir em reunião era… bom, reunião é reunião. Mas o trabalho pesado ficava na digitação. Estimativas, sprints de duas semanas, story points, tudo isso foi calibrado para um mundo em que o esforço de produzir era o esforço dominante.

    O Armin Ronacher chamou o que aconteceu de “The Final Bottleneck”: a IA inverteu a relação e agora escrever é mais rápido do que revisar. O Avery Pennarun (apenwarr) foi mais longe e argumentou que cada camada de aprovação multiplica o tempo por 10, porque a IA acelera a produção mas não acelera a revisão. E não é só impressão: o relatório da Faros AI sobre o “paradoxo da produtividade” mostra quase o dobro de PRs abertas e o tempo de revisão crescendo na mesma proporção. Produzimos mais e entendemos na mesma velocidade de antes.

    Aí vem a provocação: se um agente implementa uma feature em duas horas e a revisão leva cinco, o que exatamente estamos estimando no planning? A pergunta “quanto tempo para implementar?” perdeu relevância. A pergunta que importa virou “quanto tempo para garantir que isso está correto?”. Tem gente na AWS defendendo que o sprint planning deveria evoluir para algo como intent design: em vez de estimar tarefas, o time define intenção, guardrails e critérios de sucesso, e deixa a implementação para quem agora é rápido nela. Eu não sei se o nome vai pegar, mas a ideia de que o planning deixa de ser sobre esforço e passa a ser sobre intenção me parece certa.

    Vale lembrar:

    “Computadores são inúteis, eles só sabem dar respostas”. — Pablo Picasso

    Se a IA dá respostas muito rápido o trabalho de fazer as perguntas certas e conferir se a resposta faz sentido continua sendo nosso, e ele não ficou nada mais rápido.

    A curva de custo, de novo

    Voltemos à curva do Boehm. Eu sempre gostei do argumento de que pair programming é a forma mais barata de revisão que existe, porque a validação acontece no exato momento em que o código nasce, lá na parte baixa da curva. O code review tradicional acontece bem mais à direita, quando a mudança já está pronta e o autor já foi fazer outra coisa.

    Com IA, o “par” passou a ser humano mais agente, e o custo desse pareamento é praticamente zero. A dinâmica inverteu: o humano vira o navigator permanente (revisa, direciona, decide) e o agente vira o driver (digita, implementa). O Kent Beck chama isso de augmented coding: você escreve o teste, a IA gera a implementação, você valida. A validação é reposicionada no ponto mais barato da curva. Para mim esse é o ponto de virada de toda a discussão.

    O problema é o que acontece do lado direito da curva. Se o volume de código gerado é impossível de revisar no ritmo em que chega, o revisor cai no dilema que chamam de cognitive debt: ou mantém o rigor de antes e vira o gargalo do time, ou aprova no ritmo que o código aparece e torce para os testes pegarem o que a revisão deixou passar. Nenhuma das opções é boa.

    E aqui tem uma coisa que a gente costuma esquecer: code review nunca foi só sobre achar defeito. Era o jeito de distribuir conhecimento sobre o codebase, alinhar decisões de arquitetura, fazer sêniors ensinarem júniors e vice-versa. Era o jeito de manter um modelo mental (aka contexto) compartilhado de como o sistema funciona. Se a máquina escreve o código e a máquina também revisa, quem é responsável? E, mais importante, como o time aprende? Aquela ideia de collective code ownership do XP não se sustenta se ninguém do time leu o código.

    Meme dos Simpsons com um jornal contendo a manchete "Old Man Yells at Cloud".

    E o “como o time aprende” não é preocupação de um programador velho rabugento gritando para a nuvem. Um estudo da Anthropic sobre formação de habilidades com IA mostrou que quem delega passivamente (pede o código, cola, segue em frente) sai com menos de 40% de compreensão do que foi feito, enquanto quem usa a IA para investigar conceitos passa dos 65%. A mesma ferramenta produz um profissional que aprende ou um que só delega, dependendo de como o processo em volta dela é desenhado. Mais um cenário onde o processo pode se tornar um problema.

    Isso muda até a definição de pronto. “Done” deixou de ser “funciona e passou nos testes” e passou a ser “funciona, eu entendo por que funciona e sei o que monitorar”. O Casey West, no Agentic Manifesto, descreve como a passagem de verificação (fez o que eu disse?) para validação (fez o que eu queria?). E o pessoal da Mozilla AI aponta que a nossa cultura sempre ligou ownership a autoria; a IA quebra esse vínculo, e agora você precisa ser dono de algo não porque você escreveu aquilo mas porque você entende aquilo.

    Pelo mesmo raciocínio, o WIP limit do Kanban precisa mudar de dimensão. Não adianta limitar quantas tarefas estão “em andamento” se um agente consegue mover dez delas para essa coluna ao mesmo tempo. O limite relevante virou “quantas mudanças o time consegue entender simultaneamente”. O projeto OpenClaw, com suas 2.500 PRs abertas, é o que acontece quando a entrada cresce mais rápido que a capacidade de processar: uma falha acumulativa.

    O que levar desses questionamentos?

    A IA não torna os processos obsoletos. Ela torna os processos errados mais perigosos, porque agora o erro é produzido em escala industrial.

    O gargalo se deslocou de escrever para compreender e validar, e os rituais precisam acompanhar esse deslocamento. A daily deixa de ser “o que eu fiz” e passa a ser “o que eu entendi sobre o que foi feito”. A retrospectiva, que sempre foi o ritual mais negligenciado, virou na minha opinião o mais importante de todos: é o único momento formal em que o time para e pergunta “qual código gerado a gente não entende? que decisões técnicas o agente tomou sem a gente validar?”.

    Também vale um alerta. Tem gente dizendo que documentação e specs agora importam mais que o código, porque viraram o insumo do agente. Pode ser. Mas se a resposta para tudo for “escreva uma spec bem detalhada antes de começar”, cuidado: isso tem nome, e o nome é Waterfall. Mais spec na frente não é sinônimo de mais controle. O que separa uma spec útil de um documento de requisitos de 1970 não é o tamanho dela, é o tamanho do ciclo até o primeiro feedback: se a spec alimenta um agente que devolve algo testável em uma hora, ótimo, isso é Ágil com outra ferramenta; se ela precisa estar “completa e aprovada” antes de qualquer código existir, é a curva do Boehm de novo, só que com um capítulo novo. Vinte anos para matar o Waterfall e seis meses para a IA trazê-lo de volta seria um final meio irônico para essa história.

    O Ágil precisa evoluir, não ser abandonado. E o jeito mais barato de começar é o mesmo de sempre: revisando as suas práticas, uma por uma, e perguntando se elas ainda estão otimizando o gargalo certo.

    Deixei de fora várias práticas que também merecem essa revisão (TDD, CI/CD, prototipação, demos, refactoring) desse texto porque ele já entrega o ferramental para refatorar os seus processos. Uma lista mais extensa pode ser encontrada nos slides da minha apresentação na Codecon 2026.

  • AutoCV: meu micro-SaaS

    Faz tempo que não aparecemos por aqui, não é? Mas acho que o motivo do retorno é bacana.

    O motivo desse post é anunciar o lançamento do meu primeiro micro-SaaS, o AutoCV.

    O AutoCV é um serviço online para facilitar a sua vida naquele momento em que você está procurando uma nova oportunidade de trabalho (que é um nome bonito para emprego… trampo… corre…). Com o AutoCV, você pode gerar o seu currículo/resume personalizado para o cargo que uma vaga pede e, se necessário, gerar uma carta de apresentação (cover letter) específica para aquela vaga. O sistema usa IA para gerar o conteúdo à partir dos dados da sua carreira, que podem ser cadastrados no site manualmente ou importados do PDF do seu perfil gerado pelo LinkedIn.

    AutoCV: funciona em inglês mas também em outros 5 idiomas incluindo português.

    O sistema também tem extensão para Chrome (e já está em processo de aprovação as extensões para Firefox e Safari), que permite gerar esses documentos com um simples clique.

    Extensions: Capturam a vaga e gera CV e carta de apresentação personalizada em poucos cliques

    O AutoCV funciona gratuitamente para você aplicar para aquela vaga dos sonhos, mas, no plano pago, você tem direito a cerca de 20 candidaturas por mês e aumenta as suas chances.

    Todos os documentos gerados podem ser revisados, alterados, regerados e exportados para os formatos texto (markdown, HTML e PDF). Você também pode hospedar o seu CV em uma URL pública (no plano Pro você pode personalizar o link) para enviar o link para os recrutadores.

    Enfim, acho que é uma ferramenta muito útil que eu criei para meu próprio uso e espero que ajude vocês a conseguirem a tão sonhada vaga.

    O plano Pro tem preço adaptado para o Brasil e custa R$9,99/mês ou R$49,00/ano (mais de 40% no plano anual!).

    Se você quiser aproveitar o lançamento, eu criei um link de 15% de desconto para o lançamento (válido até 24/08) no link: https://pipelink.io/@autocv-lancamento (Pipelink é o próximo produto que a gente vai lançar. Fiquem de olho.

    No próximo post, eu vou contar a saga de como esse produto foi construído e vou falar do framework de trabalho que eu estou criando para trabalhar com IA de forma mais responsável e não somente vibe-code. Esse framework está permitindo que eu crie 3 produtos simultaneamente porque padroniza vários componentes necessários para que um SaaS funcione.

  • Dominando Processos Seletivos

    Dominando Processos Seletivos

    Artigo baseado na apresentação feita durante a PythonBrasil 2025 em São Paulo.

    Por Que Isso Importa

    Dominar processos seletivos é uma habilidade específica — e essa habilidade não tem nada a ver com trabalhar. Você pode ser um excelente profissional e ser péssimo em processos seletivos, e vice-versa. São competências distintas.

    As coisas são como são

    Esse texto não vai tentar idealizar o assunto. Os processos seletivos são injustos, falhos e desgastantes e nós, candidatos, sempre estamos na situação mais vulnerável.

    Aceitar isso é o primeiro passo. O jogo tem regras, e conhecê-las é a sua vantagem.

    Marketing & Venda

    No processo seletivo o produto é você. E como qualquer produto, ele precisa ser bem posicionado e bem apresentado.

    Durante um processo seletivo, o candidato precisa demonstrar interesse genuíno pela vaga (nem que seja pelo salário), que se preparou para aquela oportunidade específica, que tem as habilidades necessárias para o trabalho, que vai entregar o que é esperado, que cresce continuamente, e que sabe trabalhar com pessoas.

    Cada ponto desses não precisa ser declarado explicitamente, precisa transparecer na sua postura e nas suas respostas ao longo de todo o processo.

    Todo processo seletivo envolve muitas pessoas e uma característica humana importante é a de gostar de ouvir histórias. Por conta disso, uma forma bastante efetiva para trabalhar o seu marketing e seu processo de vendas é o storytelling.

    Se você conseguir embalar suas qualidades, suas limitações e seus diferenciais dentro de boas histórias, as suas chances aumentarão absurdamente.

    A Preparação

    Existe uma expressão antiga que diz:

    “Me dê seis horas para derrubar uma árvore e eu passarei as quatro primeiras afiando o machado.” — Abraham Lincoln(?)

    Todo bom profissional precisa investir tempo na preparação. Uma boa preparação poupa muitas dores de cabeça e frustrações na nossa carreira e não é diferente para encarar uma jornada de processos seletivos.

    “Cabeça”

    O ponto mais importante de toda a preparação é mental: prepare-se para ser rejeitado. Processos seletivos são, na prática, um teste da sua capacidade de lidar com rejeição. O processo inteiro é feito de rejeições, afinal, quando você para de ser rejeitado é porque o processo está terminando.

    A minha dica é tentar encarar cada “não” como “alguma outra pessoa tinha uma correspondência melhor com essa vaga”, não como “você é incapaz”. Você não é um conjunto de habilidades laborais. É uma dica mais fácil de oferecer do que praticar mas com o tempo você vai ficando melhor nisso.

    Currículo e Presença Online

    Tenha um Curriculum Vitae base genérico (ex. “Desenvolvedor Backend”), sem nível hierárquico, de uma ou duas páginas, focado no seu principal ponto forte, com contatos e sem firulas. Esse CV servirá de base para ser adaptado conforme a vaga.

    Seu perfil no LinkedIn deve estar em Português e Inglês, com as experiências descritas usando a Técnica STAR (mais sobre ela abaixo). Complete o perfil com um hub de redes sociais profissionais como site pessoal e perfil no GitHub. Lembre-se de manter somente conteúdo profissional nessas redes. Política eleitoral, futebol e tretas ficam fora.

    Não é obrigatório mas pode ser adequado fechar o seu perfil em suas redes sociais pessoais durante essa etapa. Não é incomum que recrutadores dêem uma espiada nas redes sociais pessoais dos candidatos. Eu sei que isso não é legal e não deveriam influenciar na sua candidatura mas, como eu disse, as coisas são como são e (ainda) não é o momento de lutar para mudá-las.

    Técnica STAR para Descrever Experiências

    A Técnica STAR é a forma mais eficaz de descrever experiências técnicas, tanto no LinkedIn quanto em entrevistas. A sigla representa Situação, Tarefa, Ação e Resultado:

    • Situação: O sistema de deploy da empresa era manual e causava erros frequentes.
    • Tarefa: Automatizar o processo para reduzir falhas e tempo de entrega.
    • Ação: Implantei um pipeline de CI/CD no GitHub Actions com testes automatizados e integração ao Cloud Run.
    • Resultado: O tempo de deploy caiu 70% e os incidentes pós-release reduziram em 90%.

    O padrão funciona porque força você a contextualizar o problema, mostrar sua contribuição concreta e quantificar o impacto, exatamente o que um recrutador ou entrevistador quer ouvir.

    Carta de Apresentação

    Tenha um modelo de carta de apresentação pronto para ser personalizado. Um modelo eficaz é simples e direto:

    Olá [nome do(a) recrutador(a)],
    
    Meu nome é [...] e estou entrando em contato sobre a vaga [...] pois acredito que tenho as habilidades necessárias para contribuir com a [nome da empresa] no desenvolvimento de produtos como [lista de produtos
    que você buscou no site da empresa].
    
    Gostaria de participar do processo seletivo e ter a oportunidade de descrever minha experiência em [lista de experiências específicas relevantes para a vaga].
    
    Em anexo segue meu CV e meu perfil no LinkedIn pode ser visualizado em [...].
    
    Obrigado,
    [seu nome]

    Organizando os Processos

    Mantenha um Kanban com as colunas Vaga, Contatado, Em Processo, Resultado. Em cada card, registre o link da vaga e o nome do contato.

    Use uma agenda dedicada exclusivamente para compromissos de processos seletivos e nunca deixe uma entrevista passar despercebida. Perder a hora de uma entrevista pode ser fatal.

    Equipamento para Entrevistas Remotas

    Mesa, cadeira e computador: nada de celular.

    A máquina deve estar estável (nada de software Alfa/Beta), com terminal e navegador abertos e prontos para uso, ou seja, esteja com o machado afiado e pronto para a ação mesmo se tentarem te pegar de surpresa.

    A câmera deve estar posicionada na frente ou levemente acima do rosto, para que você olhe o recrutador nos olhos.

    A iluminação deve ser direta para o rosto. A luz natural de uma janela funciona bem mas não descarte a possibilidade de uma iluminação dedicada.

    Para o áudio, use microfone, fone ou headset com fio: bluetooth acabando a bateria no meio da entrevista é constrangedor.

    Mantenha um fundo limpo ou um background virtual estático e neutro. Se tiver um fundo que deixe alguns hobbies aparentes tudo bem. Mas lembre-se: esses elementos do fundo podem causar distração no recrutador e tirar ele do que realmente importa: você.

    Entre na sala de reunião alguns minutos antes e teste imagem e som. Tenha papel e caneta à mão para anotar perguntas.

    A Prospecção

    Onde Buscar Vagas

    O LinkedIn, ao menos para programadores, continua sendo a fonte mais eficaz. O plano Premium ajuda, mas mesmo sem ele dá para filtrar boas oportunidades. Em seguida vêm os sites de vagas, depois networking e eventos, e por último indicações.

    As vagas por indicações geralmente têm as melhores chances mas infelizmente são mais raras.

    Tipos de Oportunidades

    Nem toda vaga é igual. Eu classifico as vagas em três categorias:

    • Vagas dos sonhos: são aquelas onde a empresa, o projeto ou o salário são exatamente o que você quer, mas a aderência às suas habilidades ainda não é total.
    • Vagas qualificadas: são muito alinhadas com o que você sabe fazer, mas sem muita empolgação com a empresa ou remuneração.
    • Vagas fraudulentas: geralmente são criadas para aplicação de golpes ou criação de base de dados. Fuja delas.

    O Processo

    Defina um número máximo de processos simultâneos: se estiver desempregado, quatro é um bom limite; se estiver trabalhando, dois. Gerencie tudo no Kanban e revise todos os cards toda manhã.

    Nunca se atrase para compromissos, mas esteja preparado para atrasos do recrutador. Sempre mencione que você está em outros processos, mesmo que não esteja. Se estiver empregado, garanta que o recrutador saiba disso também.

    Seja transparente com os recrutadores sobre o andamento desses processos e caso tenha algum imprevisto que te impeça de participar de alguma reunião avise o recrutador o mais rápido possível.

    A Entrevista

    Storytelling

    A melhor forma de lidar com uma entrevista é embrulhar ela em uma boa história que descreva a sua jornada com seus avanços, obstáculos, superações, etc. Lembre-se que o recrutador é um ser humano como a gente.

    Então, ao interagir com ele, apresente-se profissionalmente e construa uma narrativa de crescimento com as suas experiências. Se for seu primeiro emprego ou uma mudança de área, experiências indiretas também valem.

    Respeito também é fundamental. O recrutador é um trabalhador tal como você e não um adversário.

    Se você fica nervoso com entrevistas eu recomendo que você treine com um amigo antes de entrevistas importantes e não use LLMs durante a conversa, a menos que o entrevistador peça explicitamente.

    Durante a entrevista tem uma regra: não minta. Mas se precisar omitir algo, vá em frente. “Enfeitar” pode 😛

    Tenha “portos seguros” preparados para perguntas difíceis. Tenha um bom exemplo de trabalho em equipe, de superação, de aprendizado técnico, de colaboração, de projeto bem sucedido e de projeto fracassado. Quando uma pergunta difícil chegar é só sacar a carta certa e mostrar.

    Invente um motivo alinhado com o contratante para o seu interesse na vaga: “preciso pagar as contas” tem que virar “vi que vocês estão trabalhando bastante com a tecnologia X e é exatamente a área em que quero focar minha carreira”.

    Nunca fale mal de experiências anteriores. Resignifique essa experiência para algo como “uma experiência importante onde cresci muito com a ajuda do time”. Uma demissão injustificada se transforma em algo que “te deixou triste, pois tinha planos de ficar por muito tempo”.

    Tenha também uma explicação natural para períodos de desemprego: estudando um idioma, sabático, empreendendo, etc.

    Se for um problema de saúde pode mencionar mas lembre-se que ainda existe muito preconceito com alguns tipos de problemas (principalmente psicológicos/psiquiátricos). É uma merda, eu sei, mas… “As coisas são como elas são”.

    Sempre que a entrevista abrir espaço, faça perguntas sobre a empresa. Ao menos uma pergunta para cada área:

    • Pessoas: tamanho e perfil do time, organização das equipes, plano de carreira.
    • Processos: metodologia ágil, pipeline de tarefas, time de produto.
    • Tecnologia: stack, CI/CD, modelo de deploy, testes automatizados, code review.

    Red Flags na Entrevista

    Alguns comportamentos durante a entrevista indicam uma vaga problemática (aka vaga arrombada):

    • Perguntas sobre filhos ou planos para tê-los (especialmente para mulheres)
    • Comentários ou perguntas sobre aparência física
    • Questões de foro íntimo: relacionamento, moradia, política
    • Perguntas sobre saúde
    • Histórico de layoffs recorrentes na empresa
    • Sinais de precarização do contrato: PJ obrigatório, sistemas de tracking de trabalho, cultura de “trabalho duro” como valor

    Se mesmo assim você precisar muito da vaga: omita planos de maternidade/paternidade, use roupa neutra, evite comentar sobre família ou tratamentos. E comece a procurar outro emprego assim que for contratado.

    O Teste

    Existem diferentes tipos de entrevistas técnicas e dinâmicas de avaliação técnica. Se algumas empresas adotam uma entrevista simples outras poderão pedir para você programar alguma coisa (live coding) ou até mesmo entregar algum projeto completo.

    Entrevista Técnica

    Tudo o que falamos sobre participar de uma entrevista com um recrutador e a importância de conduzir um bom storytelling se aplica também na entrevista técnica.

    Para ter um bom resultado nessa fase é importante que você chegue sabendo tudo sobre as tecnologias usadas pela empresa. Também é importante que você “perceba” o seu entrevistador, por exemplo, programadores podem ser péssimos entrevistadores, ou o entrevistador pode ter menos experiência do que você. Nesses casos é importante que você faça a entrevista render bem. Você precisa ser mais proativo em expor suas habilidades.

    Alguns entrevistadores querem ver como você trabalha; outros se importam mais com o seu conhecimento sobre ferramentas específicas.

    Em toda a entrevista técnica é importante sempre pensar em voz alta. Mais do que saber sobre as coisas que você já sabe o entrevistador também está avaliando sua capacidade de pensar sobre os assuntos.

    Ao longo da minha experiência eu consegui distinguir três tipos de perguntas técnicas nessa fase:

    • Perguntas abertas: perguntas tais como “como você modelaria um CRUD de Clientes?” pedem que você percorra um raciocínio, não que chegue a uma resposta perfeita.
    • Perguntas fechadas: são aquelas como “o que esse código imprime na tela?”. Elas têm uma resposta certa. Para esse tipo de pergunta é possível responder “não sei, precisaria consultar” sempre que você não souber a resposta.
    • Perguntas opinativas: são feitas para saber se suas opiniões estão alinhadas com os valores da equipe, por exemplo, “o que você acha de SOLID?”. A resposta adequada para essas perguntas pedem que você seja genérico e equilibrado: “sei o que é, já trabalhei em projetos que usam e em outros que não. Como em tudo, tem prós e contras.”

    Duas respostas curingas que salvam: “Não sei, mas imagino que…” e “Depende, pode me falar mais sobre…”.

    Por último é importante evitar polêmicas sobre as escolhas técnicas da empresa, mesmo que eles deem abertura.

    Live Coding

    Treino é treino e jogo é jogo. Live coding é jogo. Pratique em plataformas como LeetCode, HackerRank, CodeWars ou Advent of Code.

    No momento da prova, pergunte se o entrevistador quer ver testes automatizados ou só o código. Se ele pedir testes esteja preparado para escrever testes.

    Como eu disse logo acima é importante que você diga tudo o que está pensando e fazendo em voz alta para o avaliador conseguir entender o seu jeito de trabalhar.

    Quando estiver trabalhando no problema sempre faça o código funcionar primeiro, mesmo com um algoritmo ingênuo ou ineficiente, para depois tentar otimizar e melhorar. Deixe claro para o avaliador que você vai fazer isso: “vou resolver dessa forma mais simples para garantir que entendi o problema; depois eu otimizo.” Se conseguir explicar a ineficiência em termos de complexidade algorítmica (Big-O), melhor ainda.

    Teste de Projeto

    Este tipo de avaliação está caindo em desuso, mas ainda pode ser encontrada por aí.

    Sempre “cobre” para fazer o teste. Empresas estrangeiras pagam por eles mas se a empresa não paga pelo teste peça que pelo menos você coloque o resultado no seu portfólio pessoal no Github. E fique atento a empresas que usam testes como trabalho gratuito.

    Faça somente o que o enunciado pede, ou seja, cuidado com over-engineering. É muito comum que o candidato, por querer mostrar todos os seus conhecimentos de Design Pattern, SOLID, etc encha o código de complexidade desnecessária.

    Foque no que é importante e poucas pessoas fazem: documentação, testes automatizados, arquivos de configuração, e padronização de código.

    Esse projeto costuma subsidiar alguma entrevista futura então esteja consciente do que você programou e lembre-se que é muito difícil ter essa consciência quando o código foi escrito por um modelo de IA.

    Red Flags no Teste

    Fique atento a três comportamentos problemáticos durante avaliações técnicas:

    • Dogmatismo: “só um modelo/linguagem/arquitetura presta, o resto é lixo” sem uma explicação ou justificativa sólidas.
    • Desleixo: desprezo declarado pelas boas práticas de engenharia de software.
    • Compartimentação extrema: times ou indivíduos muito segregados em funções específicas, o famoso “super-herói”.

    A Negociação

    Passadas todas as etapas do processo é chegada a hora da conclusão e da negociação.

    Em Caso de Recusa

    Agradeça o tempo e a oportunidade e não se esqueça de pedir feedback técnico: “poderia me dizer quais pontos não me qualificaram para que eu possa estudar mais sobre eles?” Pergunte sobre futuras oportunidades: “existe alguma política para aplicar a vagas futuras?”

    O objetivo dessas perguntas é simples: registrar que você realmente queria a vaga.

    Em Caso de Contratação

    Ninguém está fazendo favor para ninguém. O seu trabalho tem valor e precisa ser remunerado adequadamente.

    A pretensão salarial é sua.

    Diga ela, qualquer que seja. É função do contratante decidir o que fazer com essa informação. Negocie sempre o salário e trate benefícios, bônus, ações/options, e outros adicionais como brindes.

    Sobre PJ vs. CLT

    Faça as contas direito considerando rescisões, impostos, multas e garantias. Pergunte-se sempre o motivo pelo qual o empregador prefere o risco trabalhista de uma contratação PJ? Porque vai te pagar mais ou porque vai gastar menos?

    Se a empresa tiver um orçamento menor do que sua pretensão, negocie revisões salariais preventivamente: “aceito entrar por X, mas quero uma garantia para chegar em Y em Z meses.”


    Créditos da imagem em destaque: JOB INTERVIEW ..” by Bill Strain, CC BY 2.0

  • A Bunda do Cavalo do Unix

    A Bunda do Cavalo do Unix

    Tem uma anedota que correlaciona o tamanho dos ônibus espaciais à largura da bunda dos cavalos. Se você pesquisar por esses termos vai encontrar várias referências a essa história que, claramente, é uma bobagem.

    No mundo da computação a gente tem algumas dessas características que influenciam nossas vidas até hoje. Uma das mais óbvias é o teclado QWERTY que usamos até hoje (aliás, a história das teclas serem afastadas para evitar enroscos também é potencialmente falsa).

    Muitos anos atrás, antes mesmo de eu ter sequer nascido, existiam computadores gigantescos chamados mainframes e uns computadores “menores” que eram chamados de minicomputadores.

    Apesar de serem “mini” eles ainda eram enormes para os nossos padrões atuais.

    Uma coisa interessante dos computadores dessa época é que a gente precisava usar um outro equipamento para interagir com eles: o terminal.

    Os primeiros terminais se pareciam muito com uma máquina de escrever ou, para quem não conhece uma máquina de escrever, é só imaginar um teclado montado junto com uma impressora.

    Você digitava os comandos para o computador no teclado e esses comandos eram impressos em um papel que vinha de uma espécie de bobina contínua (ou em formulário contínuo com picote, remalina, etc). As respostas do computador também eram impressas nesse formulário.

    Para quem se perguntou por que os caracteres ASCII especiais \r se chamam “carriage return” (retorno de carro), \n “new line” (nova linha), entre outros, é porque eles comandavam essa impressora a retornar o carro para a coluna inicial e avançar o papel para uma linha nova em branco.

    Com a evolução da tecnologia os terminais foram evoluindo e deixaram de usar papel para usar vídeo e, hoje, textos dentro de uma janela de um aplicativo de… “terminal”.

    O sistema operacional Unix (papai do Linux e vovô do macOS) nasceu no mundo dos minicomputadores e foi desenvolvido inicialmente com terminais convencionais chamados de teletype (teletipo).

    O Unix mapeia os dispositivos em arquivos. E o arquivo para mandar informações de/para um terminal TTY (teletype) é /dev/tty até os dias de hoje mesmo usando um terminal dentro de uma janela do nosso ambiente gráfico.

    Nessa mesma época os usuários do Unix precisavam editar arquivos para fazer coisas básicas como, por exemplo, programar. O editor que eles usavam era o editor (sim, eles eram pouco criativos para nomes).

    Imagine que em um teletipo não tem o conceito de navegar com um cursor pelo texto. Toda a edição era feita através de comandos para o ed.

    Uma das coisas que todo editor precisa oferecer é uma ferramenta de busca de texto em um arquivo inteiro. Para fazer uma operação em um arquivo inteiro no ed eles usavam o comando g (global).

    Para fazer uma busca de um texto não tem nada mais poderoso do que usar uma expressão regular (sim, elas já existiam naquela época). Então a gente delimitava uma expressão regular com um par de barras: /.../. Por último, a gente precisava instruir o ed sobre o que fazer quando ele encontrava o texto procurado. Uma ação comum era pedir pro ed imprimir a linha, ou seja, (print).

    Então recapitulando os passos ficaria: global, /regular expression (re)/, print.

    g/re/p

    Se precisasse fazer isso na linha de comando, que nome eu daria para esse comando? grep!

    E se eu precisasse aplicar comandos do ed em um stream de texto ao invés de só interagir com um arquivo? Eu criaria um stream editor ou somente sed.

    Como vocês puderam ver, decisões de muitos anos atrás ainda influenciam o nosso dia a dia até hoje.

  • Gramática e Programação

    Gramática e Programação

    Eu sempre detestei aprender línguas. Só me esforço para aprender o básico de um idioma quando realmente não há outra forma de sobreviver sem ele. Foi assim que acabei aprendendo o suficiente de inglês e português.

    Lembro que, na escola, eu odiava as aulas de português. A única parte que eu realmente curtia era Literatura. Todo o resto era um verdadeiro martírio.

    Mas o destino, com seu senso de humor peculiar, colocou meus dois professores de português como vizinhos de muro. Em anos pares, eu tinha aula com uma professora; em anos ímpares, com um professor.

    Como eu sempre ficava de recuperação nessa matéria, minha mãe acabava pagando aulas particulares — com o professor do outro ano. O resultado? Eu tinha o dobro de aulas de português com os dois vizinhos ao longo do ano inteiro.

    Por conta disso, acabei aprendendo algumas coisas “úteis completamente inúteis”. Por exemplo, sei que toda paroxítona terminada em ditongo crescente era acentuada, mas que, com a nova reforma ortográfica, isso deixou de ser verdade. Se antes “idéia” tinha acento, agora não tem mais.

    Outra coisa que aprendi, à base de muita dor e sofrimento, foi análise sintática. Toda aquela parte que disseca as sentenças e mostra como elas são compostas: os sujeitos e predicados, os verbos com suas “transitividades”, os objetos, e por aí vai. Tudo isso foi entendido, ainda que sem compreender o mais importante: por que, afinal, eu estava aprendendo isso?

    Nessa época, eu já sabia programar e até tinha começado a trabalhar profissionalmente com programação (sim, comecei bem antes da lei permitir). Mesmo assim, eu não conseguia “conectar os pontos” e enxergar as relações entre a gramática e a programação.

    Programação

    Programar é comunicar. E, para comunicar, precisamos de uma linguagem comum entre todos os envolvidos no processo.

    Ao longo dos anos, a computação desenvolveu uma infinidade de linguagens de programação, cada uma com suas próprias características e propósitos. Essas linguagens facilitam a comunicação entre programadores e computadores, além de permitir que os programadores se entendam entre si.

    Por exemplo, qualquer programador que conheça Python e um computador com um interpretador Python será capaz de entender e executar códigos escritos nessa linguagem.

    Assim como os idiomas e linguagens naturais, as linguagens de programação possuem gramática. Elas se preocupam com sintaxe, semântica, pragmática, entre outros aspectos que fazem parte do estudo das línguas. Além disso, também possuem um léxico e vocabulário próprios que, se não estão estritamente sob o guarda-chuva da gramática, fazem parte do domínio linguístico.

    Python, assim como muitas outras linguagens de programação, é uma linguagem imperativa. Isso significa que você escreve comandos para instruir “como” as coisas devem acontecer. Nesse paradigma, o foco está em ações e etapas específicas.

    Por outro lado, linguagens que seguem outros paradigmas — como funcional, declarativo, lógico ou de marcação — não se preocupam tanto com o “como”, mas sim com o “o quê” deve ser feito.

    Aqui é possível traçar um paralelo interessante entre o paradigma imperativo na programação e o modo verbal imperativo na língua portuguesa: ambos lidam com comandos e ordens. No português, a conjugação dos verbos no imperativo é usada para instruir ou demandar ações — exatamente o que fazemos ao escrever código em uma linguagem de programação imperativa.

    Tá, e daí?

    Se programar é comunicar e usamos linguagens para transmitir algo, será que os conhecimentos de gramática de uma linguagem natural (português, inglês, alemão, etc.) podem ajudar a melhorar o nosso código?

    Eu acredito que sim. E, a seguir, vou listar algumas práticas que adoto ao desenvolver meus projetos.

    Vale lembrar: estou falando de projetos de software escritos em linguagens de programação imperativas, não de uma obra literária. As sugestões que vou apresentar podem melhorar o seu código, mas provavelmente seriam péssimas escolhas para seu conto, romance ou artigo acadêmico.

    Sentenças simples

    Escrever sentenças simples facilita a compreensão. A estrutura mais básica de uma sentença segue o padrão:

    Sujeito — Verbo — Complemento

    O sujeito pode assumir várias formas, mas geralmente é representado por um substantivo (nome/noun). Em linguagens orientadas a objeto, o sujeito é (quase) sempre um objeto. Assim, uma sentença simples em código poderia ser algo como:

    objeto.verbo(complemento)  # ou...
    objeto.verboComplemento()

    Parece óbvio, não? Sim, é básico, mas dito dessa forma, fica claro que os métodos de um objeto devem necessariamente ser verbos, e que os parâmetros passados ao método funcionam como complementos — nos moldes da análise sintática da transitividade dos verbos:

    • Verbo Transitivo: Requer um complemento (objeto direto ou indireto) para completar seu sentido.

    Exemplo: “Ela comprou um livro.” (complemento: “um livro”).

    • Verbo Intransitivo: Não precisa de complemento, pois possui sentido completo por si só.

    Exemplo: “Ela dormiu.”

    Se o verbo usado no nome do método for transitivo, é importante lembrar que ele exige um complemento para que a ação faça sentido. Essa escolha ajuda a tornar o código mais intuitivo e compreensível.

    Single Responsibility

    Observe que a sentença usada como exemplo contém apenas um verbo. Mantendo essa estrutura, também garantimos que cada método realiza apenas uma ação:

    1 verbo → 1 ação → 1 responsabilidade

    Trabalhar dessa forma nos ajuda a garantir que nossos métodos respeitem o Princípio da Responsabilidade Única (Single Responsibility Principle – SRP), que estabelece que cada método ou função deve ter apenas uma responsabilidade clara e bem definida.

    Interfaces REST HTTP

    Interfaces são um meio de disponibilizar o acesso a componentes do seu sistema. Existem diversos tipos de interfaces que podemos utilizar, e uma delas são as APIs REST, que usam o protocolo HTTP.

    O protocolo HTTP trabalha de forma stateless: um cliente faz uma requisição (request) para um recurso/documento/objeto em um servidor, e o servidor retorna uma resposta (response) para o cliente. O formato de uma requisição HTTP é aproximadamente assim:

    METHOD /url
    Header1: ...
    HeaderN: ...
    
    <payload>

    Alguns dos METHODs disponíveis são: GETPOSTPUTPATCHDELETE, etc. Perceba que todos os métodos são verbos, ou seja, indicam ações requisitando algo do servidor. Seguindo nossa lógica gramatical, podemos inferir que a /url é o complemento (geralmente representado por um substantivo).

    Como mencionado anteriormente, é importante manter as sentenças simples, ou seja, com apenas um verbo por requisição. Uma boa interface deveria seguir este padrão:

    GET /users/1
    POST /users
    DELETE /users/c0ffe

    No entanto, é comum encontrar requisições que apresentam dois verbos na mesma sentença:

    POST /contracts/1/remove
    GET /documents/2/cancel

    Percebe como isso soa estranho? Casos como esse geralmente aparecem quando a ação envolve processos mais complexos. Por exemplo, cancelar um contrato pode exigir várias etapas, e um simples DELETE /contracts/1 pode não ser suficiente. Quando isso acontece, uma boa prática é transformar o segundo verbo em um substantivo, tratando o processo como um objeto.

    Por exemplo, ao invés de usar algo como:

    POST /contracts/1/cancel  # verbo + verbo

    Eu prefiro reestruturar assim:

    POST /contracts/1/cancellation  # verbo + substantivo

    Essa abordagem não só torna a requisição mais natural, como também reflete melhor a lógica gramatical e mantém a consistência da interface.

  • Economia dos Testes

    Economia dos Testes

    No mundo dos testes automatizados, os testes são classificados por níveis de validação. Testes que validam uma pequena unidade de código são chamados de testes unitários (unit tests). Quando validam a integração entre componentes, são chamados de testes de integração (integration tests).

    Esses dois tipos de testes são os mais comuns. No entanto, também existem testes que simulam a interação real do usuário com a aplicação, conhecidos como testes end-to-end.

    Cada tipo de teste gera custos de desenvolvimento, manutenção e execução ao longo da vida de um sistema. Em linhas gerais, quanto mais simples e pequeno um teste, mais barato é escrevê-lo e mantê-lo. Por exemplo, testes unitários são mais baratos que testes de integração.

    O tempo de execução dos testes varia e isso também influencia no custo dele. Testes unitários são rápidos, enquanto testes funcionais ou end-to-end demoram mais devido à etapa de preparação que costuma ser bem longa.

    Testes bem escritos custam mais para serem desenvolvidos, mas reduzem os custos de manutenção. O oposto também é verdade: testes mal escritos podem até ser mais baratos para serem escritos, mas custam caro para serem mantidos.

    Os testes unitários são fáceis de escrever e manter se o código estiver bem desacoplado, pois testam apenas uma pequena unidade de código. Testes de integração, embora mais caros, garantem que a comunicação entre partes do sistema funcione corretamente sem que o tempo de execução aumente.

    Com os testes funcionais ou testes end-to-end avaliam a capacidade do software de resolver problemas reais. Como o nome diz eles testam se o software “funciona”. Embora desejáveis, são difíceis de escrever corretamente e são os mais caros de desenvolver, manter e executar.

    Entendendo a relação entre o custo e os benefícios que cada tipo de teste trás, como distribuir o “orçamento” que você tem para escrever testes?

    Minha estratégia

    Minha estratégia favorita é escrever muitos testes unitários, alcançando alta cobertura.

    Exemplo: 235 passed in 100s; 99.98% coverage

    Também escrevo testes de integração para cenários ideais (happy path) e de falhas esperadas.

    Exemplo:

    1. Teste acessar perfil público do usuário @publico
    2. Teste falha ao acessar perfil privado do usuário @privado
    3. Teste falha ao acessar qualquer perfil sem autenticação

    Não faria um teste de integração para “Falha ao acessar um usuário com nome inválido” porque um teste unitário já deveria garantir isso. Se a regra mudar, não preciso alterar testes em vários lugares.

    Para os testes funcionais ou end-to-end, foco nos fluxos fundamentais do sistema.

    Exemplo:

    1. Fluxo de Cadastro (sign up): se esse fluxo quebrar, novos clientes não serão convertidos.
    2. Fluxo de Registro (sign in): se esse fluxo quebrar, clientes não conseguirão usar o produto.
    3. Fluxo de Compra (check out): se esse fluxo quebrar, em um e-commerce, não venderíamos.

    Ou seja, escrevo testes funcionais ou end-to-end apenas para garantir que, se algo falhar, a aplicação não se torne completamente inútil.

  • Transpire Qualidade

    Transpire Qualidade

    Na biografia do Steve Jobs escrita pelo Walter Isaacson, tem um trecho em que o biografado fala sobre a influência que o pai dele deixou para ele na criação de produtos.

    Ele conta que o pai gostava de fazer alguns trabalhos de marcenaria por passatempo e que um dia ele estava corrigindo um pequeno defeito na parte interna de um guarda-roupa que ele havia terminado de construir.

    O Jobs vê o pai investindo um esforço enorme naquele conserto, pergunta:

    — Pai, esse defeito está do lado de dentro… ninguém vai ver ou se importar com isso.

    No que o pai dele responde algo como:

    — Eu vi e por isso me importo.

    O mito e as histórias criadas sobre o Steve Jobs mostram que ele sempre foi obsessivo pelos detalhes dos produtos que ele ajudou a criar até os mínimos detalhes internos das placas de circuito impresso.

    Ele fazia isso porque ele entendia que o cuidado com as todos os aspectos do produto “transpiravam” para a qualidade geral dele. Ele também fazia isso porque era um tremendo babaca, mas isso a gente pode discutir em outro momento.

    Seu código transpira

    Várias vezes ao longo da minha carreira esbarrei com empresários, gestores, profissionais da área de produto e até mesmo desenvolvedores que diziam que a qualidade do código não era tão importante quanto o fato do produto estar “funcionando” e “entregando valor para o cliente”.

    Será mesmo? Será que teu cliente sabe que aquele software porcaria que ele está usando poderia ser muito melhor, mais eficiente, mais estável, etc.?

    Vamos ser honestos… vocês não estão de saco cheio de usar software lento e cheio de problemas? Vocês já tentaram comprar passagem aérea recentemente? Fazer um PIX no aplicativo do seu banco? Já tentaram resolver um problema com seu pedido em um site de e-commerce?

    Sabe porque a nossa vida é miserável assim? Porque toda a engenharia de software está mais comprometida em desenvolver software do que refatorar software.

    Afinal de contas, todos os “stakeholders” estão interessados em vender mais, faturar mais, aumentar o “market share” e se, por exemplo, aquele sisteminha de rastreamento de encomenda está mal-feito ou com uns bugs que afetam só 1% dos usuários pode deixar lá.

    O custo de refatorar esse sistema para ele funcionar melhor seria muito alto e esses recursos seriam melhor empregados implementando um sistema de venda de publicidade para aumentar as vendas no site, não é mesmo? E aqueles 1% dos usuários afetados pelo problema já compraram, não é mesmo?

    Para os “stakeholders” é melhor investir em uma funcionalidade que aumenta o faturamento em 5% imediato do que arrumar um sistema que afeta somente 1% do que já foi faturado.

    Aquele pequeno “TODO”

    E se você está pensando que estou falando apenas dos grandes sistemas e serviços, você está enganado. Estou falando daqueles “TODO” e “FIXME” espalhados pelo seu código.

    Estou falando daquele backlog de consertos e refatorações que nunca é priorizado pelo time. Daquele código “vizinho” ao seu que está cheio de problemas e que não será melhorado porque está fora do escopo da sua tarefa.

    Quando eu trabalhava diretamente com software livre e open-source era muito comum você encontrar melhorias para fazer no código conforme você ia desenvolvendo seu próprio código. Se você mandasse seu código sem melhorar o outro código vizinho provavelmente rejeitariam sua contribuição até que você fizesse tudo direito.

    Fazer isso em uma empresa tem se mostrado quase impossível.

  • Publique-se!

    Publique-se!

    Eu produzo conteúdo técnico sobre programação e carreira desde que esse negócio de “produzir conteúdo” se chamava blogar.

    Nunca fui um blogueiro prolífico, mas sempre tentei deixar meus aprendizados e ensinamentos públicos ao longo do tempo. O meu blog pessoal mudou de nome e propósito várias vezes nessa jornada. Ele já se chamou “aCiDBaSe” (o meu ‘nome hacker’ da era IRC), Pythonologia (na minha época “Python Promoter”), Blog da Triveos (quando tive minha empresa) e, hoje, ele é só um humilde blog WordPress com 5 plugins e tema padrão rodando na minha humilde (mas excelente) hospedagem (link com meu referral) compartilhada onde também hospedo meus e-mails longe de provedores que podem decidir fechar minha conta unilateralmente.

    Sempre desejei produzir conteúdo e compartilhar as coisas que aprendi. Mas desenvolver a disciplina para fazer isso com regularidade e uma boa frequência sempre foi um problema.

    Ainda não sou o cara que gostaria de ser e não produzo tudo o que gostaria de produzir, mas recentemente eu comecei a me sentir melhor comigo mesmo nessa tarefa e, como consequência, a disciplina foi melhorando.

    Como sou um nerd que gosta de listas, eu vou enumerar aqui as mudanças que fiz na minha vida e nos meus processos que fizeram tudo ficar melhor e mais fácil:

    Me respeitar

    Eu me sentia mal por não produzir conteúdo. Me pressionava o tempo todo sobre isso e me sentia mal quando não conseguia corresponder às minhas próprias expectativas.

    Quando comecei a trabalhar isso na terapia, eu fui me dando conta que isso não é uma obrigação e que não atender a essa necessidade era algo compreensível. A angústia foi diminuindo e hoje eu estou bem melhor com o volume de coisas que faço.

    Significa que é fácil ou que tiro isso tudo de letra? Nem de perto. Mas quando a angústia e a ansiedade surgem eu consigo buscar a origem dela e trabalhar melhor o problema.

    Remover distrações

    Eu comecei a fazer isso bem antes do meu diagnóstico de TDAH, mas certamente foi um divisor de águas no processo. Mesmo sem saber do TDAH, eu já tinha percebido que uma fonte de impedimentos para minha produção eram as distrações.

    Que tipo de distração? Com ferramentas.

    Yak Shaving

    Toda vez que eu tinha uma ideia sobre algo para postar no blog, eu entrava na interface de admin, atualizava tudo, trocava o tema, tentava exportar o conteúdo para outro CMS, testava YA-ferramenta-geradora-de-sites-estáticos, testava um plugin novo, conferia o SEO e analytics do site… um processo infinito de Yak Shaving.

    Como eu disse acima: hoje eu uso WordPress na minha hospedagem (que oferece one-click-installation de WordPress) no meu domínio onde já rodo meus e-mails. Deixo autoatualização ligada para tudo. Tenho só uns poucos plugins de segurança e backup ligados. Coloco tudo por trás do Cloudflare (pacote gratuito) para ter `https` sem ter que ficar instalando/renovando certificados SSL. E uso o tema padrão do WordPress (que muda nos updates major e eu só aperto “accept” do meu lado).

    Não tenho que criar, habilitar, configurar, etc. nada para colocar o site no ar e publicar meus textos. Nada de GitHub Actions, static file generation, S3, Cloudfronts, CDNs, etc. (exceto o Cloudflare por comodidade para ter https).

    Também não tenho Analytics instalados exceto por um bem básico que vem no plugin Jetpack e um gerado pelo Cloudfront.

    A ideia é tornar o processo tão simples quanto clicar em “Novo Post”, escrever, e clicar em “Publicar”.

    Conforto

    Eu sempre gostei de escrever. Texto mesmo. Escrevi no meu blog, no meu livro, nas minhas redes sociais, etc. Acho que, por gostar de ler, eu sempre gostei de escrever. Na infância até ganhei uns concursos de redação do jornal da cidade.

    Mas senti que as novas gerações não estavam mais muito ligadas em conteúdo texto. Decidi me aventurar em novos meios: vídeos.

    Investi uma grana pesada porque eu sou (burro) assim: se é para fazer algo, tem que ser perfeito.

    Estúdio “profissional” com equipamento de primeira linha (na foto não aparece mas tem câmera profissional, GoPro, DJI Osmo e até um Drone eu comprei)

    Até a chácara da família foi desenhada para ser usada como um estúdio para conteúdos mais “maker” que eu pretendia (ainda pretendo) fazer.

    Futuro estúd… digo… chácara da família.

    Mas deu tudo errado. Eu descobri que não gosto de produzir vídeos. As únicas coisas dos meus vídeos que me deixavam satisfeitos eram os roteiros. Porque eram textos. Depois dessa etapa era tudo horrível. Principalmente a edição (eu odeio editar video…)

    Dei um pause (ainda não sei se é definitivo) no canal. É provável que eu volte a produzir coisas ali só quando eu quiser mostrar meus aspectos mais “make“. E quando eu tiver condições de pagar alguém para editar (repito: eu odeio editar video).

    Eu me sinto confortável com texto. É com ele que eu vou trabalhar. Se as pessoas não curtem texto é uma pena. Mas não vou sair do meu conforto para elas. E já tem muita gente boa fazendo video.

    Ótimo é inimigo do bom

    Eu sou muito chato com o que escrevo. Tudo tem que estar perfeito, completo, com referências, revisado, … Impecável. Isso é um baita muro na minha produção.

    Ter começado a publicar coisas incompletas, com erros (que são corrigidas quando apontadas), ou com a qualidade “mais-ou-menos-para-mais” e mesmo assim receber feedback positivo da audiência foi libertador para mim.

    Vencer meu preconceito com certas ferramentas, e nem estou falando de IA (ainda), me tornou muito mais produtivo.

    Ainda não consegui estudar o suficiente para adotar uma ferramenta de IA no meu fluxo de trabalho, mas é quase certo que vou adotar uma logo. Mais para me ajudar na revisão (e edição) do que na autoria. Por uma questão filosófica, eu (ainda) tenho um certo preconceito com o uso desses geradores na autoria do meu conteúdo (ele seria meu ainda?).

    Eu já falei um pouco sobre isso em um post aqui mesmo no LinkedIn. Lá eu falo sobre usar o LanguageTool para revisar e eliminar erros mais básicos do texto.

    Seu conteúdo na sua casa

    Sempre que o conteúdo que eu produzo se apresenta de uma forma relevante, eu publico ele no meu blog, no meu domínio, na minha hospedagem (ok, não é minha, mas eles não são uma big-tech que ganha dinheiro com meu trabalho).

    Esse artigo foi publicado inicialmente no LinkedIn, mas logo coloquei ele aqui também. Na verdade, esse é o primeiro artigo que escrevo primeiro no LinkedIn e só fiz isso porque queria conhecer melhor a ferramenta deles (curti).

    Mas a minha recomendação é: tudo o que você produz deveria estar na sua “casa”. E a sua casa deveria ser uma casa própria.

    Publique e mantenha seu conteúdo no seu site, na sua hospedagem, no seu domínio, sob suas regras e sob seu controle.

    Conhecimento precisa ser livre

    Esse item se relaciona um pouco com anterior porque quando produzimos um conteúdo em uma plataforma proprietária como aqui, Medium, Twitter, YouTube, Spotify, etc. a gente entrega o nosso trabalho para um terceiro que passa a ter mais controle do que você sobre esse trabalho.

    Não se aprisione. Use essas redes e plataformas para referenciar ou até mesmo replicar o seu trabalho.

    Não recomendo muito a replicação por questões de SEO e “findability“, mas se você estiver buscando uma presença nessas plataformas a replicação pode ser uma boa estratégia.

    E, quando estiver com seu conteúdo no ar, torne ele facilmente compartilhável. Se você faz seu conhecimento facilmente compartilhável, ele alcança mais pessoas e circula mais livremente.

    Mas ele tem que ser compartilhável por meios abertos e com padrões abertos também. Não somente em redes fechadas e proprietárias.

    RSS (Really Simple Syndication)

    A maneira que eu defendo (e promovo/agrego) o compartilhamento do seu conteúdo é através do RSS. Mas eu só considero um RSS correto quando ele é completo e não somente um resumo com um link de “leia mais/read more”.

    Aaron Swartz, grande ativista do compartilhamento livre, um dos criadores do RSS

    Todos os sistemas de publicação sérios suportam RSS (ou Atom que dá no mesmo).

    Disponibilizar seu conteúdo por RSS/Atom é mais do que só compartilhar. É um ato político no sentido de retomar a Internet das mãos das grandes empresas de tecnologia.

    O formato RSS foi co-criado pelo ativista Aaron Swartz, que acreditava e defendia a liberdade e a democratização do acesso ao conhecimento. Ele morreu por isso.

  • Nem certo e nem errado

    Nem certo e nem errado

    Se tem uma área do conhecimento onde os debates são quase sempre bem acalorados, essa área é a do desenvolvimento de software. Tem todo tipo de disputa, das mais bobas como Tab vs. Espaço, temas claros ou escuros, 80 colunas ou não, etc. até as mais complicadas: tipos estáticos ou dinâmicos (ou qualquer variante doida), OO ou funcional, código aberto ou software livre, etc.

    Em comum com todas essas discussões é a tendência entre os debatedores de apelar para conclusões binárias: “é isso aqui ou está errado.”

    Essa postura também aparece quando as pessoas que estão habituadas a um conjunto de conceitos precisa se adaptar a outros conceitos. Um exemplo: a pessoa que usa Windows (ou Linux) reclamando do botão de maximizar janela do macOS. Ela com certeza vai reclamar e dizer que aquilo está errado. Mas se você lembrar que a primeira empresa a lançar uma interface baseada em janelas para o grande público foi a Apple e só depois o Windows e o Linux surgiram fazendo diferente, quem está “errado” nessa história?

    No meio do desenvolvimento de software, talvez porque computadores eletrônicos que usamos sejam binários, adoramos a segurança das respostas absolutas. Isso é certo. Aquilo é certo. Isso é errado. Aquilo é errado.

    No mundo Python onde eu trabalho tem até um termo (que eu detesto) para isso: pythônico. Todo mundo ouve, mas ninguém sabe o que é.

    Mas o tempo me ensinou que as coisas podem simplesmente “ser”. Nem certas e nem erradas. Provavelmente diferentes, provavelmente similares.

    Indiferente das alternativas se parecerem ou não, uma coisa é certa: cada uma das opções tem os seus pontos favoráveis (prós) e seus pontos desfavoráveis (contras). E, quando escolhemos uma opção, temos que renunciar às outras. É a tragédia (e a beleza) da vida.

    Entrevista de emprego

    Toda essa introdução serviu para falar sobre uma coisa que eu sempre faço quando estou entrevistando um candidato para uma vaga: perguntas específicas com poucas alternativas de respostas onde nenhuma das alternativas é certa ou errada.

    Vou dar um exemplo com a minha famosa pergunta sobre ORMs.

    Um ORM (Object-Relational Mapper) são softwares que servem para conectar o mundo dos objetos em um sistema orientado a objetos aos registros (linhas, tuplas, etc.) de um banco de dados relacional.

    Esse tipo de software é bem complicadinho de se escrever porque o modelo relacional tem algumas diferenças fundamentais com o modelo orientado a objetos e lidar com essas diferenças sempre força o desenvolvedor a fazer certas escolhas em detrimento de outras.

    Não vou detalhar muito essas diferenças e nem as alternativas aqui porque elas não são tão importantes para a pergunta e nem para a resposta, mas recomendo a leitura dos artigos que falam sobre esse tópico. Certamente vai te tornar um programador melhor.

    Quando estou com o candidato eu mando um diálogo parecido com esse:

    — Você sabe o que é um ORM?
    — [se o candidato disser que não, eu pulo para outra pergunta parecida, senão eu prossigo… se o candidato estiver meio nervoso, eu também aviso que a próxima pergunta não tem uma resposta certa e nem errada e que eu só quero entender como ele pensa sobre programação orientada a objetos].
    — Suponha que eu te peça para implementar um ORM em Python para usarmos em nosso sistema e eu te mostre dois ORMs diferentes para você se inspirar (ex. Django, SQLAlchemy). Tudo bem?
    — Sim… entendo…
    — No ORM do Django, se eu quero persistir um objeto no banco de dados, eu faço: modelo.save(). Mas no SQLAlchemy, se eu quero fazer o mesmo, eu faço algo parecido com storage.add(modelo). Ou seja, no Django o método que persiste o modelo no próprio modelo e no SQLAlchemy a responsabilidade de persistir o modelo é do objeto Storage. Qual desses modos você escolheria?

    E aí o candidato teria algumas opções de resposta:

    1. Não sei, preciso estudar mais as opções: boa resposta, mostra que entrei num assunto que ele desconhece, mas que ele tem interesse em se aprofundar.
    2. Eu faria igual ao Django: resposta boa. Mas me leva à linha de contestar a escolha e perguntar se não seria responsabilidade demais para o objeto modelo cuidar das regras de negócio e da persistência dos dados no banco?
    3. Eu faria igual ao SQLAlchemy: resposta boa também. Mas eu contestaria a escolha perguntando se a opção do Django não seria mais simples e conveniente para o uso mais comum onde precisamos somente gravar um objeto no banco sem ter que se preocupar com storages e afins?
      • Algumas vezes recebi boas respostas onde o candidato implementaria o ORM para funcionar no modo SQLAlchemy, mas implementaria um método helper .save() também nos modelos que faria algo do tipo: storage.add(self). Não posso negar que eu ficava bem feliz com essa resposta porque seria a minha resposta (mostrando que ela é a resposta certa 😛).
    4. Eu faria diferente de ambos e faria [assim]: pode ser uma resposta boa aqui, mas, na prática, eu nunca recebi uma alternativa muito consistente nesses casos. Quase sempre foram respostas que mostravam o desconhecimento do candidato sobre o que é e como um ORM funciona.

    Notem que a pergunta é só um ponto de partida para entender como o candidato pensa e faz as suas escolhas quando está programando. Quando ele faz uma escolha e, portanto, uma renúncia, eu apresento sempre um contraponto à escolha dele para compreender se ele fez essa escolha porque ele julga ela a escolha certa e a outra errada ou se ele, de fato, ponderou as diferenças, prós e contras das opções apresentadas para dar a resposta.

    Profissionais que sempre estão refletindo sobre seu próprio trabalho estão sempre em crescimento. Aqueles que estão presos aos seus dogmas e tradições sempre estarão limitados por conceitos colocados por terceiros como sendo o certo.

  • Código Cabuloso

    Código Cabuloso

    Esse artigo é quase um repeteco dos artigos Dicas para um bom programa em Python e o Personal Python Style Guide. Mas aqui mostro como uso o processo de refatoração do código para estudar e entendê-lo.

    Estou tentando entender um código bem intrincado e importante do trabalho… é um código bem crítico que resolve um problema bem difícil (merge de objetos) e foi desenvolvido “sob demanda”, ou seja, fizeram uma versão básica e foram incrementando ele com cenários diversos que foram sendo descobertos com o tempo.

    Todo mundo na empresa tem medo de mexer com esse código porque, apesar dele ter até uma certa cobertura de testes, não sabemos se esses testes realmente cobrem todos os cenários reais.

    Mas preciso entender esse código para fazer uma otimização (ele executa um UPDATE muito demorado no nosso banco de dados e eu preciso remover esse UPDATE).

    Eu não sei onde esse UPDATE acontece porque o código é todo elaborado com execuções tardias (lazy) das operações. Então preciso ler tudo para entender onde essa operação está sendo agendada para execução.

    Como tenho TDAH é muito difícil, para mim, somente ler o código para entendê-lo. Quando o código é curto e simples tudo bem, mas não é esse o caso. O que eu geralmente faço é um processo de refatoração do estilo do código. Não só em termos de formatação (porque tem ferramentas para isso que já rodam no nosso sistema de integração contínua), mas também em estilo estrutural.

    Vou separando cada bloco de refatoração ou função refatorada em um commit em um branch criado especificamente para esse trabalho.

    Uma vez que terminei tudo e entendi o funcionamento do código crio um PR (em modo draft) com uma descrição detalhada do que fiz, da separação em commits, necessidade de revisar, como revisar, etc. para meus colegas de trabalho avaliarem e até mesmo responder algumas das minhas dúvidas. Mas o mais importante: inicio a descrição do PR explicando que a aceitação dele é totalmente opcional e até mesmo não indicada por conta dos riscos envolvidos.

    Para todo esse processo é imprescindível o uso de uma ferramenta de refatoração automática que possibilite renomear identificadores, extrair funções/métodos, inversão de lógica em ifs, etc. Senão o seu trabalho será miserável.

    Munido de todos os requisitos passo a alterar o código da seguinte maneira…

    Nomes melhores

    Entender um código onde os identificadores são chamados obj, data, tmp, etc. é complicado. Ter identificadores com nomes como “foo_json” que tem um dict() e não uma JSON-string também não ajuda muito.

    Renomeie variáveis, funções, métodos, classes, etc. para terem sempre o nome correto. Se estiver difícil escolher um nome para o identificador é porque o código tem outro tipo de problema ou ainda falta compreensão sobre ele.

    Early Return

    Uso early return pattern para reduzir o volume de indentação do código e o embaraçamento dele (tangled). O objetivo é linearizar os fluxos e criar blocos segmentados com lógica de processamento. Sou um “Never Nester Developer”. 😀

    Como já comentei nesse artigo aqui, de modo bem simplificado, o código abaixo:

    def f(c1, ..., cN):
        if c1 and... cN:
            ... faz um monte de coisas ...
            return 1
        return 0

    Vira algo do tipo:

    def f(c1, ..., cN):
        # cenário excepcional 1
        if not c1:
            return 0
    
        ...
    
        # cenário excepcional N
        if not cN:
            return 0
    
        ... faz um monte de coisas ...
    
        return 1

    O código fica mais longo, mas é possível identificar bloco a bloco quais são as condições excepcionais da função em cada bloco.

    Lembrem-se que o objetivo aqui não é eficiência e sim a legibilidade e compreensão do código.

    Ajustes de if‘s, elif‘s e else‘s

    Nessa etapa o objetivo é eliminar o máximo de if‘s, elif‘s e else‘s do código (e adicionar else‘s quando temos algum elif‘s inevitável).

    Uma forma de fazer isso é inicializando valores em certas variáveis e só modificá-los dentro do if. Mas nem sempre isso basta e, em alguns casos, quando cada bloco é muito grande ou tem chamadas de funções, etc. sequer é possível de ser feito.

    Um exemplo bem simplório só para ilustrar o que estou dizendo:

    if cond:
       v = f()
    else:
       v = default

    Vira algo tipo:

    v = default
    if cond:
        v = f()

    Dessa forma deixo no fluxo normal a condição padrão e crio um branch só para tratar de uma excepcionalidade.

    Essa refatoração pode realmente ficar enorme e o resultado também pode ficar pior que o original, logo, use com moderação.

    Outra refatoração que faço não tem relação somente com a compreensão do código, mas até mesmo com o funcionamento correto dele: se você tem if e elif é prudente ter um else. Mesmo que seja para levantar um erro. Afinal, se você pensou em mais de um cenário, o que acontece com aquele cenário que você não pensou?

    Falo sobre isso abordagem aqui e aqui.

    Extração de código

    Outra refatoração que ajuda bastante a melhorar a legibilidade do código é conhecida como Extract Method. Ela possibilita trocar um trecho de código por uma chamada de função que descreve o que esse código faz.

    Para fazer essa refatoração é bem útil ter uma ferramenta que automatize o processo. A IDE que uso no dia a dia oferece essa refatoração, mas é provável que existam plugins para vários outros editores e IDEs.

    Essa é fácil de ilustrar:

    def f(x, y):
        # Verifica se o objeto x é válido
        valid = False
        if x.a and x.a == 0:
            valid = True
    
        if x.compare(y):
            valid = True
    
        if valid:
            ... faz algo ...

    Vira algo assim:

    def is_valid(x, y):
        if not x.a or x.a != 0:
            return False
    
        if not x.compare(y):
            return False
    
        return True
    
    
    def f(x, y):
        if not is_valid(x,y):
            return
    
        ... faz algo ...

    Quando você está lendo o código de f(x,y) você, sabe que o objeto x é validado primeiro e o resto da função só será executado quando o objeto x for válido.

    Exceções devem ser exceptions

    É muito comum ver funções retornando flags (ex. None) quando ela precisa sinalizar um problema, um erro ou uma exceção.

    Considerando que idealmente uma função (ou método) deve retornar sempre objetos de um mesmo tipo, o retorno de None deveria ser algo ruim, certo?

    Quando retornamos None em nossas funções precisamos ficar verificando todos os valores retornados antes de usá-los, ou seja, toda hora vemos os famigerados:

    ret = f()
    if ret:
       ... faz algo ...
    else:
       ... trata o erro ...

    Em linguagens com suporte a exceções podemos usá-las para sinalizar problemas ou… excepcionalidades!

    try:
        ret = f()
    exception UmaExcecaoBemEspecificaQueFPodeGerar:
        ... trata o erro ...
    
    ... faz algo ...

    Essa refatoração melhora a legibilidade do código porque deixa o tratamento da exceção bem perto do código que pode gerar ela. E para isso ser verdade é importante que o bloco try/except realmente seja pequeno e restrito ao trecho onde a exceção pode acontecer.

    Também é importante que a exceção gerada (e tratada) sejam sempre bem específicas para o erro gerado para evitar tratar o erro inadequadamente.

    Usar bem a linguagem

    O código da empresa onde trabalho é escrito em Python e então eu refatoro ele para ficar mais “pythônico” (o que quer que isso signifique para mim).

    Legibilidade é melhor que eficiência nesse momento.

    Prefiro um belo “if-zão” bem legível a uma “ifexpression” toda muvucada.

    Um loop pode funcionar melhor que um comprehension… estou ajustando o código para ler e entendê-lo e não para que ele rode um femtossegundo mais rápido.

    Tipos

    Tipos (e eventualmente anotação de tipos) podem auxiliar na compreensão do código, bem como as ferramentas de refatoração automática.

    Tente padronizar os tipos de parâmetros e retornos das funções. Tente fazer com que eles sempre recebam e retornem objetos dos mesmos tipos. E lembre-se também que None é do tipo NoneType e não do mesmo tipo dos objetos que você está querendo usar. 😜

    Uma função que busca uma pessoa pelo nome:

    def get_pessoa(name):
       pessoas = Pessoa.filter(name=name)
       if not pessoas:
           return None
       return pessoas[0]

    Ficaria assim:

    def get_pessoa(name: str) -> Pessoa:
        pessoas = Pessoa.filter(name=name)
    
        if not pessoas:
           raise PessoaNaoEncontrada(name)
    
        if len(pessoa) > 1:
           raise MultiplasPessoasComNome(name)
    
        return pessoa

    Namespaces para contextualizar

    Quando esbarro com muitos nomes que vem do mesmo módulo, tento refatorar o uso deles para incluir o nome do módulo de origem no namespace:

    from contants import (
        FOO,
        BAR,
        BAZ,
        QUX,
        DUX,
    )
    
    def f(x):
        if x == FOO: ...
        if x == BAR: ...
        if x == BAZ: ...
        if x == QUX: ...
        if x == DUX: ...

    Vira:

    import constants
    
    def f(x):
        if x == constants.FOO: ...
        if x == constants.BAR: ...
        if x == constants.BAZ: ...
        if x == constants.QUX: ...
        if x == constants.DUX: ...

    Dessa forma trago o contexto de qual módulo os identificadores vem.

    Métodos próximos dos objetos

    Esse faço pouco porque o time onde trabalho não curte “Fat Models” do mesmo jeito que eu gosto.

    Mas essencialmente transformo quaisquer funções auxiliares que lidam especificamente com um tipo de objeto em um método do próprio objeto e tiro da frente especificidades que aquele objeto pode encapsular para mim.

    Mover trechos de código e funções que lidam com um tipo específico de objeto como método do próprio objeto.

    def nome_completo(pessoa):
        return f"{pessoa.primeiro_nome} {pessoa.ultimo_nome}"
    
    def etiqueta(pessoa):
        return {
            "nome": nome_completo(pessoa),
            "endereco": pessoa.endereco,
        }

    Fica:

    class Pessoa:
        @property
        def nome_completo(self):
            return f"{self.primeiro_nome} {self.ultimo_nome}"
    
    def etiqueta(pessoa):
        return {
            "nome": pessoa.nome_completo,
            "endereco": pessoa.endereco,
        }

    Assim, ao analisar a função etiqueta() foco especificamente em como ela funciona sem me distrair com código de concatenação de nomes.

    OOP e não DOP

    Dicionários são estruturas de dados tão poderosas em Python que é bem fácil a gente começar a usá-las para tudo em nosso código. Mas isso começa a se tornar um problema com o tempo porque é quase impossível encapsular os dados de um dicionário com o objetivo garantir a consistência deles.

    Enquanto estou fazendo a refatoração de um código, começo a usar namedtuples, dataclasses ou até mesmo uma classe “convencional” (com métodos e tudo) para substituir os dicionários que ficam espalhados pelo código.

    def grava_pessoa(dados_pessoa: dict) -> int:
        if not valida_dados_pessoa(dados_pessoa):
            raise DadosInvalidos(dados_pessoa)
    
        # se tem 'id' já existe no DB
        if dados_pessoa.get("id"):
            return update(dados_pessoa)  # retorna o id
    
        return insert(dados_pessoa)  # retorna o id

    Ficaria mais ou menos assim:

    class Pessoa:
        def __init__(self, nome, ...):
            self.nome = nome
            ...
    
        def valida(self):
            ...  # valida dados do objeto
    
    def grava_pessoa(pessoa: Pessoa) -> int:  # ou Pessoa
        if not pessoa.valida():
            raise DadosInvalidos(pessoa)
        
        if pessoa.id:
            return update(pessoa)  # retorna id ou Pessoa()
    
        return insert(pessoa)  # retorna id ou Pessoa()

    A lógica de validação do objeto não fica me distraindo do que a função grava_pessoa() faz: inserir ou atualizar os dados da pessoa no banco de dados.

    TODO/FIXME

    Uso comentários TODO/FIXME com dúvidas que não consigo solucionar lendo somente o código que estou mexendo e que não posso esquecer de perguntar para algum colega em algum momento no futuro (lembrem que tenho TDAH e esquecerei as dúvidas que tenho).

    # Ignora registro de pessoas afetados pelo bug de migração
    if 32000 > pessoa.id > 10000:
       return

    Provavelmente vira algo assim:

    # TODO (osantana): o código abaixo ainda é necessário mesmo
                       após a migração?
    # Ignora registro de pessoas afetados pelo bug de migração
    if 32000 > pessoa.id > 10000:
       return

    Estilo de código

    Por último, quando não vejo muita coisa para melhorar no código, eu mudo só algumas coisas menores no estilo de código para forçar algum tipo de formatação pelas ferramentas automáticas de formatação.

    # formatador faz algo assim:
    x = funcao_com_nome_longo_e_muitos_parametros(
            a="foo", b="bar", c="baz", d="qux)

    Coloco uma “,” para forçar a indentação abaixo:

    # formatador faz algo assim:
    x = funcao_com_nome_longo_e_muitos_parametros(
        a="foo",
        b="bar",
        c="baz",
        d="qux,
    )

    Conclusão

    Vocês têm alguma outra coisa que você também faça para poder entender algum código mais cabuloso?