De estagiário a engenheiro principal de produto: como projetos paralelos impulsionaram minha carreira

Sou apaixonado por tecnologia a vida toda. Comecei com Turbo Pascal quando criança e, desde então, trabalhei com sistemas embarcados, desenvolvimento front-end/back-end e projetos de machine learning.

iOS/macOS entrou em cena durante um curto curso universitário. Em um mês, eu já tinha aprendido uma nova linguagem e conseguido um estágio na Readdle. Cinco anos depois, me tornei Principal Engineer. Ainda assim, continuo mentorando novos colegas de equipe, participando de entrevistas e desenvolvendo habilidades de liderança para continuar crescendo.

Minha jornada, saindo de zero experiência com plataformas Apple até chegar ao meu cargo atual, me inspirou a refletir sobre minha abordagem de aprendizado e a destacar as principais direções que me ajudaram a crescer. Acredito que minha experiência pode ser valiosa para outros engenheiros que se sentem travados ou inseguros sobre seus próximos passos.

Estas são as principais abordagens e recursos que me ajudaram e que talvez também possam ajudar você.

Como projetos paralelos aceleraram meu crescimento

Depois de alcançar o nível pleno, atingi um platô profissional. Eu sentia que concluía tarefas de forma rápida e eficiente, corrigia bugs sem dificuldade e tinha uma compreensão sólida de como projetar a arquitetura de novos recursos. Mas eu não conseguia entender exatamente o que me separava de me tornar um verdadeiro Engenheiro Sênior.

A mudança aconteceu gradualmente. Olhando para trás, percebi que a diferença não era um grande projeto no trabalho nem uma promoção. Foi todo o tempo que passei programando fora do trabalho.

À noite e nos fins de semana, eu criava pequenos projetos, às vezes só por diversão, às vezes para testar uma ideia. Esses projetos paralelos me expuseram a problemas do mundo real fora do meu escopo habitual. Essa experiência prática se traduziu diretamente no meu trabalho e me deu uma vantagem ao lidar com problemas desconhecidos.

A maior lição foi que a maioria das habilidades em desenvolvimento de software não está vinculada a uma única linguagem ou plataforma. Alguns exemplos:

  • Mesmo que você esteja lendo sobre arquitetura de apps para iOS/macOS, provavelmente reconhecerá padrões semelhantes no desenvolvimento web ou em sistemas embarcados.
  • O conhecimento de Python no contexto de machine learning pode ajudar você a escrever rapidamente o script necessário para automatizar processos de CI em um domínio completamente diferente.
  • Fundamentos de sistemas operacionais vão ajudar você a entender classes inteiras de problemas em qualquer linguagem de programação. Afinal, qualquer programa, independentemente da linguagem, no fim das contas interage com o sistema operacional de maneira semelhante.
  • Um dos primeiros livros que li foi Effective Java—uma linguagem que não uso há anos. Mas esse livro me ensinou princípios de orientação a objetos que se aplicam de forma ampla. Esse é o tipo de conhecimento que permanece.
  • Se você gosta de programar, esses projetos paralelos não vão parecer trabalho. Na minha experiência, os melhores engenheiros são aqueles que programam porque amam construir e aprender.

Formas de aprender qualquer coisa

Algumas pessoas parecem aprender mais rápido do que outras. Pode ser uma nova linguagem de programação, um padrão, uma arquitetura ou até algo totalmente não relacionado ao desenvolvimento, como aprender a jogar xadrez. Acredito que isso acontece porque aprender em si é uma habilidade—uma meta-habilidade que você pode praticar.

Refletindo sobre minha própria jornada de aprendizado, percebi padrões recorrentes na forma de abordar novos tópicos. Acho que isso vem do meu período na escola e na universidade, quando conciliar várias disciplinas levou meu cérebro a otimizar padrões semelhantes. Isso me permitiu aprender mais rápido.

Hoje, tenho um processo específico para aprender algo novo. Não acho que esse processo seja revolucionário, mas ele me permitiu manter o foco e a intencionalidade tanto no aprendizado quanto no desenvolvimento pessoal. Nesta seção, vou apresentar as três etapas da minha abordagem de aprendizado: Triagem, Estruturação e Prática.

Triagem

Uma boa fonte de informação é um primeiro passo crucial ao aprender uma nova habilidade. Aqui estão algumas recomendações com base na minha experiência:

  • A maior parte das informações sobre qualquer tema está disponível gratuitamente. Raramente há necessidade de pagar por cursos ou palestras, a menos que você tenha absoluta certeza de que o conteúdo é único. Mas, se você está começando, ainda não terá a experiência necessária para distinguir insights genuínos de material de baixa qualidade.
  • Recursos em texto geralmente são mais concisos e mais fáceis de assimilar do que vídeos, especialmente se você lê rápido. Com o tempo, aprendi a ler rapidamente e filtrar informações irrelevantes, algo que é mais difícil com conteúdo em vídeo.
  • Com o tempo, desenvolvi a capacidade de passar rapidamente pelo conteúdo e filtrar informações irrelevantes, o que não é possível com materiais em vídeo, porque eles são lineares e consomem tempo. Também é mais fácil marcar ou copiar um trecho no texto do que procurar o momento certo no vídeo.
  • Recursos em inglês são muito mais fáceis de encontrar do que recursos em outros idiomas, especialmente quando se trata de programação e tecnologia. Por isso, conseguir ler e entender inglês rapidamente é indispensável!
  • Não pule os clássicos. Livros recomendados repetidamente (como Clean Code ou Design Patterns) muitas vezes ainda são o melhor material disponível.
  • Também recomendo montar sua própria biblioteca pessoal de livros sobre temas diferentes. Assim, você sempre terá material confiável à mão. Pessoalmente, mantenho uma pasta simples no meu computador.

Estruturação

Depois que reúno algumas fontes sólidas, estruturo as informações. Esse processo inclui:

  1. Processar as fontes. Ao ler um livro ou artigo, destaco os pontos principais com marcadores ou os copio para minhas notas para consulta futura.
  2. Fazer referência cruzada das informações. Especialmente quando o tema é subjetivo, é muito importante comparar várias soluções e perspectivas. Informações que coincidem em várias fontes diferentes provavelmente são verdadeiras. Quando as fontes entram em conflito, normalmente adio a decisão até adquirir mais experiência pessoal com o tema. Depois, com uma compreensão mais profunda, consigo escolher a melhor abordagem.
  3. Centralizar o conhecimento. Para analisar todas as informações recebidas e encontradas, elas precisam ser armazenadas em um só lugar. Uso o Obsidian para centralizar meu conhecimento. É gratuito, e considero o formato markdown muito prático.
  4. Armazenamento. Normalmente salvo artigos e links úteis em listas simples em markdown. Isso me ajuda a encontrar rapidamente um recurso específico sem ter que vasculhar o histórico do navegador. Além disso, se eu voltar ao tema no futuro, já terei uma boa base salva.

Prática

Ler não basta. Você só aprende de verdade quando constrói algo. Tento reforçar cada tema que estudo criando pequenos projetos. Quando se trata de programação, isso geralmente significa criar uma aplicação simples que demonstre minha capacidade de aplicar as habilidades necessárias. A prática rápida também revela imediatamente quaisquer lacunas de compreensão. Muitas vezes achei que entendia tudo o que precisava, só para me deparar com problemas inesperados apenas 10 minutos depois de começar a implementação.

Um exemplo que se destaca é aprender arquiteturas de UI. Quando estudei MVVM pela primeira vez no contexto do desenvolvimento iOS, achei que a arquitetura era simples e direta. Mas, quando tentei criar um app básico de lista de tarefas, imediatamente encontrei uma lacuna no meu conhecimento. Os recursos explicavam como a View se comunica com a ViewModel e como a ViewModel interage com a Model, mas nenhum deles abordava quem deveria criar o quê e como!

Minha aplicação tinha uma tabela exibindo as tarefas atuais da lista. Eu deveria criar uma ViewModel separada para cada tarefa? Ou uma única ViewModel para a tabela inteira? E se eu precisasse de ambas as ViewModels? Quem cria qual delas? Essas perguntas abriram a porta para entender como coordenar componentes em arquiteturas semelhantes.

Mais tarde, enquanto trabalhava no PDF Expert, nossa equipe decidiu tentar implementar MVVM. Consegui estruturar a arquitetura e ajudar a liderar a equipe no desenvolvimento desse recurso. Esse é o tipo de experiência que eu não teria adquirido se antes não tivesse criado sozinho aquele pequeno app bagunçado de lista de tarefas.

Minhas recomendações para projetos paralelos

  • Encontre inspiração e uma ideia. A ideia geralmente vem primeiro, e depois eu a conecto a um objetivo específico de aprendizado. A ideia não precisa ser única nem particularmente útil, porque o objetivo principal é ganhar experiência. Por exemplo, eu poderia decidir criar um app de acompanhamento de hábitos. A partir daí, posso escolher focar em arquitetura ou usar essa ideia para projetar uma UI impressionante com animações. Ou talvez eu precise criar um servidor para esse app? Essa pode ser uma ótima oportunidade para experimentar novas tecnologias de backend.
  • Defina o que é “pronto”. Se você tentar levar todo projeto para produção, precisará dedicar muito tempo a cada um deles. Por isso, acredito que você precisa definir os marcos que quer alcançar com cada projeto.
  • Perfeito é inimigo do bom. Se você ficar preso demais tentando encontrar a solução “perfeita”, pode perder totalmente a motivação.
  • Minimize tarefas desnecessárias. A paleta de cores exata importa em um app feito para ensinar você sobre arquitetura? Ou o sistema de logging? Os projetos mais valiosos foram aqueles focados em um único objetivo de aprendizado. Quanto menos tempo eu gasto com detalhes de baixa prioridade, mais posso investir no que realmente importa.

10 tópicos que mudaram a forma como eu desenvolvo software

Aqui estão 10 áreas que explorei por conta própria e que impactaram minha carreira.

Arquiteturas de aplicações de UI

Qualquer engenheiro que já trabalhou em projetos baseados em UI conhece os padrões clássicos de organização de código. Quem nunca ouviu falar de MVC, MVP, MVVM? Na minha experiência, um conhecimento teórico simples pode dar confiança para começar um novo projeto, mas, sem experiência prática, ele costuma desmoronar ao primeiro sinal de complexidade.

Eu já achei que entendia completamente como o MVVM funciona, mas não tinha experiência para colocá-lo em prática. Percebi isso quando tentei desenvolver uma pequena aplicação em MVVM. Ficou claro que eu precisava de coordinators e routers para navegar e organizar aquela salada de classes em uma estrutura coerente. Essa experiência me ensinou uma lição importante: não existe uma arquitetura única que sirva para tudo. Uma arquitetura que funciona bem em um projeto (ou até em uma tela) pode se encaixar mal em outro contexto.

Padrões de projeto

Quem já passou por entrevistas para desenvolvedor provavelmente encontrou perguntas sobre padrões de projeto. Muitos engenheiros parecem reconhecer certos padrões na teoria, mas têm dificuldade para aplicá-los na prática. A maneira mais simples de fechar essa lacuna é criar um pequeno projeto focado na implementação de um padrão específico.

O livro original costuma usar um editor de texto como exemplo. Na minha experiência, editores de diferentes tipos de documentos exigem o uso de muitos padrões de projeto. Pode ser um editor de texto, um editor de imagens ou até um programa de criação de diagramas.

Testes

Testes de código são um pilar fundamental de bases de código estáveis. E, embora a ideia pareça simples – “basta escrever testes!” – escrever testes úteis muitas vezes está longe de ser trivial.

O principal desafio é que manter testes consome recursos da equipe. Se você precisa reescrever os testes após cada refatoração de código, o valor geral desses testes se torna questionável. Esse conceito é conhecido como Test Fragility e, na minha experiência, não é tão conhecido quanto deveria ser. Você pode aprender sobre a teoria de testes, por exemplo, neste livro.

Um forte foco em testes também afeta a qualidade do código. Em geral, um código escrito pensando em testes será mais modular e exporá uma API mais limpa e simples. É fácil experimentar testes na prática – basta integrar testes a um projeto existente ou começar um novo com foco em escrever testes.

Algoritmos e estruturas de dados

Conhecer algoritmos não é um requisito rigoroso no desenvolvimento moderno de software. Muitas empresas ainda incluem desafios algorítmicos em suas entrevistas, embora essas habilidades talvez não sejam necessárias no trabalho do dia a dia. Ainda assim, acredito que vale a pena investir tempo em aprender algoritmos porque isso é divertido e interessante. Além disso, hoje você pode explorar esses tópicos de forma interativa por meio de plataformas como o LeetCode.

Se você algum dia tiver a oportunidade de participar de competições de programação algorítmica como a ACM ICPC, recomendo muito que tente. Mesmo que esse conhecimento não pareça diretamente útil no início, técnicas de otimização de código podem ser aplicadas a qualquer projeto, e participar de uma competição em equipe provavelmente será uma experiência da qual você vai se lembrar com carinho.

Multithreading

Multithreading é um tema fascinante. Você pode passar anos escrevendo código sem entender sequer os conceitos básicos. Todo desenvolvedor sabe que bloquear a thread principal vai travar o app. Mas, se algo der errado, só desenvolvedores que realmente entendem código multithread e conseguem visualizar a linha do tempo completa dos eventos conseguem diagnosticar esses bugs evasivos, quase fantasmas.

Na minha opinião, entender primitivas de sincronização, threads e as consequências de negligenciar esses conceitos é um dos principais fatores que separam um desenvolvedor pleno de um sênior. Para entender os conceitos básicos, recomendo o livro gratuito The Little Book of Semaphores.

Para ganhar experiência prática, você pode tentar escrever um programa que execute processamento pesado em segundo plano e sincronize os resultados com a UI, especialmente se o próprio trabalho em segundo plano envolver várias threads. Simular processos físicos pode ser um ótimo ponto de partida para esse tipo de projeto.

Programação gráfica

Processadores gráficos são uma das poucas partes de um computador com as quais a maioria dos desenvolvedores raramente precisa interagir diretamente. Na maioria das aplicações, a renderização da UI acontece na CPU ou é abstraída do dispositivo GPU real, então você nem precisa pensar nisso. Mas, quando o número de elementos na tela cresce o suficiente e abstrações de alto nível não conseguem acompanhar as taxas de quadros necessárias, você precisa se aprofundar mais. Entender os problemas que as GPUs foram projetadas para resolver, bem como a capacidade de “explicar” a elas o que precisa ser renderizado, pode ajudar você a identificar soluções rapidamente em situações semelhantes.

Um projeto clássico para aprender programação gráfica é criar um game engine. Se você ama jogos de computador como eu, provavelmente já se perguntou como eles são programados. Existem muitos recursos online para aprender Metal, DirectX, OpenGL ou Vulkan. Só não espere que seu projeto se torne a próxima Unreal Engine. Mas, se você conseguir chegar ao ponto de ter criado um pequeno jogo rodando no seu próprio engine, garanto que encontrará desafios fascinantes e adquirirá conhecimento valioso.

Programação embarcada

Quando descobri pela primeira vez que até os menores dispositivos eletrônicos ao nosso redor executam seus próprios programas e processadores, fiquei imediatamente fascinado e quis tentar construir algo parecido. No entanto, desenvolver para microprocessadores traz muitas limitações. Esses sistemas normalmente têm menos de 128 kilobytes de memória, e a alocação dinâmica de memória geralmente está fora de cogitação.

Escrever e depurar código embarcado apresenta muitos desafios. Mas, como resultado, você pode ver seu código ganhar vida no mundo real. Até fazer um LED simples piscar já pareceu uma grande conquista para mim. Entender como microcontroladores funcionam é um primeiro passo importante para compreender como CPUs e sistemas operacionais modernos funcionam. Ao trabalhar com dispositivos embarcados, você pode até precisar recorrer a linguagem assembly específica da plataforma (geralmente ARM).

Hoje, começar com desenvolvimento embarcado está mais fácil do que nunca. Microcontroladores acessíveis, como a linha STM32, são um ótimo ponto de partida, e muitas placas prontas para uso vêm com programadores integrados que se conectam via USB. As possibilidades são infinitas, desde iluminação dinâmica para ambientes até criar seu próprio hub IoT de casa inteligente.

Disassembler

Às vezes, conseguir olhar um nível mais fundo no seu código pode ser inestimável. Por exemplo, no desenvolvimento para plataformas Apple, frequentemente trabalhamos com bibliotecas da Apple majoritariamente de código fechado. Quando algo não se comporta como esperado, normalmente é necessário abrir um ticket no Feedback Assistant e esperar (às vezes por anos!) por uma resposta. Mas, com a ajuda de um disassembler, muitas vezes você pode olhar por baixo do capô e entender como o código fechado realmente funciona.

Entender o código assembly do seu próprio programa também pode ser útil, especialmente no contexto de otimização. Só inspecionando o código compilado você consegue realmente saber o que seu código está fazendo. Pessoalmente, uso o Hopper Disassembler no macOS para isso.

Compiladores

O mundo da programação tem seus próprios “clubes secretos”. Se pensarmos nas pessoas cujos produtos impactam o maior número de engenheiros, inevitavelmente chegamos àquelas que constroem os compiladores por trás das nossas linguagens de programação favoritas. Até eu passar um tempo estudando compiladores por conta própria, esses programas pareciam pura magia. Mas, mesmo aprendendo o básico, a névoa mágica ainda não permite compreender totalmente o brilhantismo de quem criou linguagens de programação inteiras.

A boa notícia é que há muitos recursos e livros disponíveis sobre o tema. Dois dos meus favoritos são Engineering a Compiler e, claro, o clássico Dragon Book. Você pode mergulhar no desenvolvimento da sua própria linguagem de programação começando pelo tutorial Kaleidoscope, da LLVM. No desenvolvimento de software do “mundo real”, a menos que você seja um engenheiro de compiladores, esse conhecimento pode dar a você uma compreensão muito mais profunda do comportamento da linguagem e de erros obscuros. Além disso, a maioria dos compiladores é open source, o que permite contribuir para projetos enormes e de alto impacto. Por exemplo, contribuir para o repositório do Swift agrega muito ao portfólio de qualquer desenvolvedor.

Fundamentos de sistemas operacionais

Se existem projetos ainda mais complexos do que compiladores, sistemas operacionais são os primeiros que vêm à mente. Como desenvolvedores que passam a maior parte do tempo trabalhando em computadores, é surpreendentemente fácil esquecer que um sistema operacional é apenas mais um programa, como aqueles que nós mesmos escrevemos. Uma enorme camada de abstrações acima do hardware cria a ilusão de que cada programa em espaço de usuário roda em seu próprio processador com sua própria memória. Essas abstrações são tão precisas que um código escrito e compilado uma única vez pode rodar de forma confiável em uma enorme variedade de configurações de hardware.

O livro Operating Systems: Three Easy Pieces é um ótimo ponto de partida para entender como sistemas operacionais funcionam. E, se você tiver interesse específico nos detalhes internos de iOS ou macOS, recomendo o recurso gratuito MacOS X and iOS Internals. Entender como um SO funciona fecha a última lacuna entre o hardware e o código que escrevemos. Para um desenvolvedor, isso significa controle e compreensão quase completos do que acontece quando seu programa é executado.

Para encerrar

Olhando para trás, eu não segui um plano rigoroso. Continuei apenas aprendendo, construindo e perseguindo o que me interessava. Essa curiosidade se transformou em carreira.

Se você está confortável, mas se perguntando o que vem a seguir, tente ampliar seus horizontes. Explore algo novo. O tempo que você investe em projetos paralelos e aprendizado vai se acumular, e as distâncias entre os níveis—Júnior, Pleno, Sênior e Principal—começarão a diminuir mais rápido do que você imagina.

Sempre há mais para aprender. Mas, com cada projeto, cada erro e cada novo conceito, você está avançando. Continue construindo.

— Andrii Zinoviev, Engenheiro Principal de Produto


Quer fazer parte da nossa equipe de Engineering e impactar milhões de pessoas? Confira nossas vagas

The Readdle Team


Receba novidades e recomendações

Fique por dentro das novidades e lançamentos assinando nosso boletim informativo.

Ao clicar em “Inscrever-se”, eu concordo com a Aviso de Privacidade.