Nos próximos minutos, eu quero contar para vocês quais são os princípios de arquitetura que eu tenho usado nas minhas aplicações e que tem me ajudado a ter muito mais produtividade em vibe coding e a evitar o o temido vibe debuging, que é essa coisa de você ter que justamente e consertar algo que quebrou e que você não sabe muito bem o que quebrou, porque afinal você não sabe muito bem programar, então você não sabe identificar onde é que tá aquela coisa que quebrou. E por que que eu sei disso? Porque eu passei por isso.
Eu trabalho com tecnologia faz 10 anos. Tô indo provavelmente paraa quinta aplicação que eu desenvolvi com ferramenta de vibe coding. Então eu cada vez tenho passado menos por isso, porque eu tenho encontrado maneiras de evitar que esses erros aconteçam comigo.
Inclusive eu recentemente lancei um app chamado Epic, que ele funciona como um tech lead para coding assistant para justamente ajudar pessoas que não tenham aí um background de engenharia a fazerem apps passando menos ódio e passando menos por esse perrengue de fazer esse vibe de bugging que eu sei que é uma coisa muito muito chata. Então, até se vocês quiserem experimentar, vou deixar o link aqui na descrição para vocês conseguirem ver depois. O primeiro ponto é meio óbvio, mas é muito importante, que é você planejar o que você vai fazer.
Eh, claro que existem diferenças, tá, entre você fazer um app que você tá fazendo por hobby e um app que você realmente tem a intenção de colocar em produção. Mas se o seu objetivo é colocar algo real em produção, é muito importante. Eu diria que é, eu pelo menos não colocaria um app que não foi planejado em produção, porque a chance de dar pau é muito grande.
É claro que assim é é muito mais legal você ir vibe coding no freestyle sem planejamento nenhum e as coisas vão acontecendo. Claro, isso é sem dúvidas mais divertido, mas isso vai você vai criar uma aplicação cheia de puxadinhos e aí a chance de você ter algum problema, e são vários problemas que você pode ter, vou falar sobre isso um pouco mais adiante, é muito grande. Então, planejar é o primeiro passo.
Eu acho que uma maneira boa de você encarar o planejamento é você pensar que você comprou um terreno, tá? E você precisa construir uma casa aqui. E aí eu sei que aqui eu quero pôr uma piscina, mas eu não tenho o dinheiro para pôr essa piscina aqui ainda.
Só que eu sei que eu quero pôr uma piscina aqui. Então, tudo bem você não colocar a piscina ali ainda ainda. Só que sabe o que você não pode fazer?
Colocar uma fossa ali. Porque se você colocar uma fossa no lugar da piscina, o dia que você for colocar uma piscina, você vai ter que tirar a fossa para ir colocar uma piscina. E é aí que tem que acontecem aquelas histórias onde o negócio que você achou que ia demorar um dia demorou dois meses porque você teve que desfazer uma coisa para ir fazer outra.
Então pensem que planejamento é basicamente isso, é você entender, ter uma visão ali, né, no alto nível do que a sua aplicação vai ser e você começar o planejamento a partir dali. E acho que uma maneira eh simples, mas muito efetiva de você conseguir fazer esse planejamento é você pensar ali na aplicação que você quer fazer e você primeiro elencar todas as features que você quer desenvolver ali no no seu projeto. Então, por exemplo, tá, como que eu até como que eu faria isso?
Eu gosto de usar sempre essa formulinha assim, ó. Users should be able to. Então, os usuários deveriam poder fazer tal coisa.
Os usuários deveriam poder fazer tal coisa. E aí você vai listando tudo que os seus usuários deveriam poder fazer. Os usuários deveriam poder fazer sign e sign e eh o login e o logout.
Os usuários deveriam poder, sei lá, fazer upgrade para um plano. Os usuários deveriam poder criar projetos. Aí você vai criando todas as suas features, tá?
Lista todas elas. Depois que você listar todas as suas features, é muito importante você identificar as dependências. Então, pensa que dependência, eh, pensando aqui na na analogia da casa de novo, tá?
Para eu fazer um telhado, eu preciso de parede. Para eu fazer a parede, eu preciso de fundação. Então, qual que é a sua fundação?
Qual que é a sua feature 001, que é a primeira feature que precisa existir para que você consiga colocar outra feature em cima dela e outra feature em cima dela e outra feature em cima dela e por aí vai. Então eu vou dar um exemplo para vocês. Se você está fazendo uma aplicação web como, sei lá, por exemplo, chatpt ou você tá fazendo um CRM, enfim, provavelmente a feature 001 da sua aplicação é a autenticação, porque praticamente se não tudo o que ele fizer na sua aplicação, ele vai ter que estar logado, ou seja, ele vai precisar ali do do do da identificação do seu usuário para atribuir as coisas que ele fizer na sua aplicação ao usuário certo.
Então eu listei todas as features aqui do meu projeto e identifiquei que a feature 001 é a feature que mais tem dependência, que é a minha feitature de autenticação. Aí pode ser que a segunda feature que mais tem dependência é a de billing, por exemplo, que é você poder cobrar pelo pelo seu app. Eu não sei.
Enfim, isso vai depender muito de de aplicação por aplicação. Mas eu acho que o que eu o que eu queria trazer aqui para vocês é você sempre pensar nessa analogia de uma casa sendo construída. E aí, qual é a feature que você precisa ter embaixo para você ter a feature de cima, para você ter a feature de cima e por aí vai?
Então, faz esse planejamento. Minha dica, faça no papel. Acho que muitas vezes a gente vai para um lado assim, pegar o GPT, enfim, às vezes isso acaba complicando coisas de uma forma que não precisa.
Então, lista todas as features que você tem na sua cabeça. E aí, depois que você listar as features, você tenta do lado de cada uma link com uma outra feature, como quem diz, essa feature depende de alguma outra coisa, estar pronta para ela poder existir. E se a resposta for sim, ela tem que, essa outra feature tem que ser desenvolvida antes para você conseguir fazer a próxima, sacou?
Então, é muito importante você pensar dessa forma. Se você fizer com esse planejamento, que é o mínimo, eu acho que você já vai conseguir ter resultados muito melhores, porque você vai conseguir criar a fundação eh antes de começar a botar ali o telhado e as paredes, etc. E e sem dúvida isso vai ajudar você a fazer um um uma arquitetura muito mais organizada e com muito menor chances de você ter erros sistêmicos, tá?
Então esse é o primeiro, primeira dica que eu daria, meio óbvia, mas enfim, planeja, isso é muito importante. Um outro conceito que eu uso muito nas minhas aplicações e que eu acho que ele é muito, muito útil é o conceito dry, que é o conceito de don't repeat yourself. O don't repeat yourself, basicamente, ele diz o seguinte: se você tiver que escrever aquele código mais de uma vez, transforma ele numa variável.
Assim você não precisa escrever ele de novo. É isso que é o dry, tá? É, é muito comum para uma LLM ela ela esquecer que ela já escreveu aquele código outra vez, porque às vezes a janela de contexto dela ali não consegue lembrar daquilo que ela já escreveu.
E aí ela vai fazer o quê? Vai escrever aquele mesmo negócio de novo, simplesmente porque ela não lembra que ela escreveu aquilo antes. Imagina aqui, ó.
Então eu tenho quatro botões e nos quatro botões eu tive que dizer que a variável da cor do botão era blue. E aí aqui eu escrevi de novo blue e aqui eu coloquei de novo blue. E aqui eu coloquei de novo blue.
Que que eu fiz? Eu me repeti quatro vezes. O que que significa?
Que se um dia eu quiser transformar esse blue em verde, eu vou ter que fazer isso quatro vezes. Eu tô dando um exemplo muito tosco, tá? Mas pensem que isso pode estar repetido 100 vezes no seu código e que para você trocar essas coisas não basta você colocar um prompt ali e pedir pro seu coding assistant fazer isso para você, porque ele vai esquecer de alguma coisa e você vai ficar que nem o maluco tentando encontrar quais são essas variáveis, essas coisas que você vai ter que mexer.
Em contrapartida, olha como isso aqui seria mais inteligente. Eu tenho uma variável chamado blue e esses quatro botões importam essa variável aqui. Então, se eu quiser mudar essa variável de blue pra green, ele vai virar green sem eu ter que repetir isso quatro vezes.
Então esse é o conceito de don't repeat yourself, porque quando você consegue fazer as coisas dessa forma modular, você evita se repetir, você economiza linha de código desnecessária, porque ao invés de você ficar ali repetindo aquela mesma coisa 20 vezes, você só importa algo que já foi codado e você vai economizar tokens, porque ao invés dele ter que escrever aquilo muitas vezes ou ler aquilo muitas vezes, ele vai ter que ler aquilo ou escrever aquilo uma vez só. Olha que maravilha. Uma outra tendência que eu percebo que é muito comum nas LMS é que elas tendem a overengineer o que você pede para ela.
Por exemplo, tá? Se eu pedir para ela fazer um um sistema de signup de usuários, ela pode me entregar algo assim, né? Olha só, um monte de linha aqui.
Ou ela pode fazer isso de uma maneira muito mais simples, certo? Esse é o conceito de KISS, que a gente chama de keep it simple stupid. Então existem n maneiras de você chegar no mesmo resultado.
E como eu já percebi que as LLMs tendem a overengineer as coisas, eu sempre tento direcioná-las dizendo para elas keep it simple stupid para elas justamente eh fazer algo muito sempre, tipo assim, sempre que tiver a opção entre você complicar ou você fazer um negócio mais simples, faz o negócio mais simples, pelo amor de Deus, não inventa moda. É, e é engraçado porque tem um livro do do Reed Hastings, né, que é o o fundador do do Netflix, que ele diz que eles conseguiram até medir a produtividade dos desenvolvedores e que ele diz que um desenvolvedor que ele é muito bom, ele chega a ser 20 vezes mais produtivo que um desenvolvedor ruim. E não é porque ele consegue escrever 20 vezes mais código, é porque ele escreve menos, né?
Então, se ele consegue, sei lá, eu tenho dois desenvolvedores aqui, eu tenho, ao invés de desenvolvedor, vamos pensar assim, eu tenho duas implementações aqui que o meu coding assistant fez, uma de 1500 linhas e uma de 150 linhas. Elas fazem a mesma coisa. O qual que é melhor?
A de 150, pelo amor de Deus, ele vai gastar menos token. E na hora para escrever, né? E na hora de dar manutenção nesse código, vai ser muito fácil, muito mais fácil ele encontrar o problema em 150 linhas do que 1500 linhas.
Então o conceito de keepit simple stupid é basicamente assim: se tiver duas opções de fazer alguma implementação, escolha a mais simples. Esse é o conceito de keepit simple stupid e sempre que você puder, direciona o seu coding assistant a usar esse conceito também. Outra coisa que eu que eu sempre percebo nos coding assistants é que eles tentam prever tudo que pode acontecer.
Eh, eles são muito proativos, né? E ainda que a intenção seja boa, né? Afinal eles querem justamente te ajudar ali a a prever todos os ed cases possíveis e cenários de erros, etc.
e tal. Isso nem sempre é benéfico para você, porque muitas vezes ele vai implementar algo que você ainda não pensou muito a respeito, algo que você não precisa de fato. E às vezes, por causa desse pequeno desvio, ele pode literalmente bagunçar a sua implementação inteira, né?
E é por isso que o princípio de é tão importante, que agne significa you aren gonna need it. Então, sempre que você vai implementar algo, pensa, será que você precisa fazer aquilo ali mesmo? Porque pode ser que a resposta seja não.
E se a resposta for não, é melhor você pular aquilo para você não correr o risco de se perder em algo que a gente nem sequer precisa para aquele momento. Então vou vou dar um exemplo para vocês. Recentemente eu tava e codando o Epic, né?
o que foi o o o app que eu que eu lancei agora recentemente. E uma das funcionalidades dele é um editor de texto que parece o Notion, né, todo formatadinho e bonitinho. Então ele ele basicamente entende o que o usuário vai fazer e ele gera um PRD ali pro usuário com tudo o que o usuário precisa fazer para implementar aquele projeto.
E aí ã numa das minhas conversas ali planejando e tudo mais, surgiu a possibilidade de implementar uma feature de real time. E real time, para quem não sabe, é basicamente quando você consegue ter dois usuários mexendo no mesmo documento e você vê meio que o outro usuário mexendo ao vivo no documento que você tá mexendo. E isso é basicamente, tipo assim, cinco vezes mais trabalho do que ele ter feito sem essa funcionalidade.
Se eu não tivesse visto isso, ele provavelmente teria gastado muito tempo, muito token, eh, tentando fazer uma feature que eu, sinceramente, não precisava para aquele momento. E isso talvez teria derailed completamente a a aplicação e o desenvolvimento. Então imagina assim, ó, tipo eu tenho um um arquivo de texto aqui, né, um arquivo de código e aí aqui eu vou ter um blocão de código para fazer essa feature de real time, só que eu não preciso.
Então a chance de eu quebrar todo o resto da minha implementação, justamente porque eu fui inventar moda aqui, é muito grande. Eu frequentemente tento lembrar o coing system que tipo você não vai precisar disso, só faz o essencial e conforme você vai, né, repetindo isso e repetindo e guiando ele dessa forma, você acaba fazendo ele ficar focado em implementar só o que você precisa e não tudo que ele imagina que precisa ser implementado. Acho que isso é muito útil para mim e acredito que isso vai ser útil para vocês também.
Esse aqui talvez seja uma das melhores coisas que eu aprendi nos meus experimentos, que é a maneira como você organiza as pastas do seu projeto de código. E a melhor maneira que eu encontrei de organizar as pastas até hoje é a maneira que eu chamo de feature base folder, que é quando você agrupa todos os arquivos de código por feature e não por layer. Então, qual que é ã a diferença entre feature e layer?
Para quem não entende o conceito de layer, eu recomendo muito que vocês assistam o um vídeo que eu gravei da minha apresentação do Lovable Day, que acho que isso explica bem o conceito de de layer para quem não conhece. Eh, prometo depois gravar um vídeo só sobre isso, mas acho que ali tem bastante conhecimento para vocês. Então, layer, para quem não conhece, imagina que você tem, sei lá, signup, né?
Então, você vai ter uma layer que é a página sign. Dentro da página de signup você vai ter o componente, que é o form. Aí dentro desse desse componente você vai ter o hook que é o que captura as interações com forme de signup e por aí vai.
Eu tenho várias layers aqui, geralmente como que os projetos são organizados por layer. Então aqui eu tenho, por exemplo, isso aqui vem do do próprio Love Boy, inclusive eh eu tenho uma pasta com componentes, então eu tenho todos os componentes da minha aplicação aqui, mas percebe que esse componente pode ser o seu formulário de sign, o card do pricing, eh, boa, sei lá, settings de usuário, pode ter um monte de componente aqui. Literalmente todos os componentes da sua aplicação vão estar aqui.
Hooks? Quais hooks? Todos todos os hooks que existem na sua aplicação vão estar aqui.
E aí o que acontece? Toda vez que eu precisar editar algo em uma feature específica, então, por exemplo, eu preciso resolver um problema com a minha feature sign. Sabe o que seu coding assis vai ter que fazer?
Ele vai ter que ver todos os componentes, todos os hooks, literalmente todos os arquivos do seu projeto para ele encontrar quais arquivos pertencem a feature signup para aí sim ele poder resolver. Só que qual que é o problema disso? Token, né?
Ele vai ter que ler muito mais arquivo, ele vai demorar mais e isso, do ponto de vista de produtividade é muito ruim. Então, como que eu tenho feito isso agora, né? Eu tenho organizado por feature, que é o seguinte, eu tenho uma pasta chamada features.
Dentro dela eu tenho várias features, então, feature 1, feature 2, feature 3. Então, por exemplo, autenticação de usuário, billing, né? São as features que eu poderia ter ali, eh, entre outras.
E aí dentro dessa pasta eu tenho todos os arquivos que eu preciso para entregar aquela feature, né? Então eu tenho todos os componentes que eu uso para aquela feature, todos os hooks que eu uso para aquela feature, todos os serviços, todos os types, todas as actions porque eu uso NextJs. Eh, então eu eu consigo concentrar ali.
E aí quando eu quero mexer em alguma coisa do, sei lá, feature de autenticação, que que ele faz? Ele só tem que ler os arquivos da feature de autoenticação. Então, ao invés dele ter que ler, por exemplo, 230 arquivos, ele vai ler 10 arquivos.
Então ele vai conseguir fazer muito mais rápido, com menos tokens e a chance dele dar uma pirada e uma noinada é menor porque ele vai ter ingerido menos coisas dentro da sua janela de contexto. Então acho que isso é um pulo do gato e eu recomendo sempre que vocês consem as pastas de vocês assim, porque eu acho que o outro jeito que é você fazer por layer, ele é ótimo para humanos, né? Só que partindo do princípio que assim, se é o seu caso, né, que você tá usando AI e LLMs para escrever 100% do seu código, você não pode organizar para humanos, você tem que organizar para LLM.
E o melhor jeito, depois de todos os que eu testei para você organizar os seus projetos, é justamente você fazer assim. E chegamos ao nosso último, mas não menos importante, separation of concerns. Tem um padrão muito comum que eu que eu tenho visto aí na Telels, que é eles tentarem agrupar todo o código que pertence a coisas relacionadas dentro de um arquivo só.
Ainda que isso pareça prático ou que a primeira vista isso não pareça muito problemático, isso é um problema, tá? Porque vamos imaginar o seguinte, tá? Vamos pegar aqui, ó.
Eu tenho tudo num file só, tá? Então imagina aqui o exemplo é, eu tô fazendo uma integração com Open AI com o System Prompt que manda uma chamada ali pro Openi. Então nesse arquivo aqui eu tenho system prompt que vai pro Open eu tenho toda a config do meu modelo, qual que é o modelo, qual que é o o a temperatura, enfim, etc, as coisas ali do do da configuração que eu preciso.
E eu tenho toda a integração que eu fiz com a API do Open no mesmo arquivo, mais de 1000 linhas de código. Agora imagina você encontrar quando dá um problema, onde tá o problema em linha de código. Ou imagina você ter que, sei lá, editar o system prompt.
Ou imagina que agora você quer ter mais de um system prompt porque por algum motivo ele vai ser variável. É muito difícil você encontrar essas onde tá o problema e onde você tem que editar, seja você um humano ou seja você uma LM. Então essa não é muito uma boa prática, né?
E é justamente por isso que o separation of concerns é tão importante. E o separation of concerns, ele diz que cada parte do código tem que ter uma responsabilidade específica e bem definida. Então, ao invés de você centralizar tudo aqui, ó, no mesmo arquivo, olha como ele fica longo e olha quantos concerns eu tenho aqui dentro desse arquivo.
Eu tenho system prompt, a config e a integração. O que que seria melhor fazer? eu separar, então eu ter um arquivo pro system prompt, um arquivo para config e um arquivo para integração.
Se eu fizer isso aqui, vai ser mais fácil eu encontrar problema. Então assim, deu um problema, onde está o problema? No system prompt.
Eu só vou ver isso aqui. Eu não vou ver todos os arquivos, entende? Ou então eu quero adicionar mais no system prompt.
Onde eu vou adicionar? No system prompt. Eu não vou adicionar aqui e deixar esse negócio de 1000, vai para 1200 linhas.
Eh, então ele fica mais fácil você encontrar onde tá o problema. ele fica mais fácil você dar manutenção. E para Ll pensar, tem uma coisa que o pessoal fala muito que é context poisoning, né?
Que é quando você colocou tanto e tanto e tanto e tanta informação ou às vezes informações que não são muito boas e a começa a dar uma viajada e ela vai viajar se você colocar tudo dentro de um arquivo, porque tipo às vezes ela vai ter contexto que ela nem precisa, que vai acabar confundindo ela. Então acho que uma coisa que é muito útil é você ter esse hábito de sempre direcionar a sua LLM. para separar as coisas.
Então, uma coisa que eu sempre faço, quando eu vejo, por exemplo, que eu eh eu tô vendo ali que ele tá meio que tentando juntar conceitos numa coisa, tentando juntar concerns num arquivo só, eu peço para ele separar, eh, paraa coisa ficar um pouco mais fácil dele de dar manutenção depois. Então, eu espero que você consiga usar essas dicas de vibe coding para você ter um pouco mais de produtividade. E então não se esquece, tá?
Eh, começa planejando, identificando as features que você precisa fazer antes de fazer outras, né, para você identificar as dependências e se planejar primeiro. Lembra sempre do dry don't repeat yourself. Então, se você colocar o mesmo código em dois lugares, transforma ele numa variável para você não ter que ficar se repetindo.
Não esquece do kiss keep it simple stupid. Então, se tiver duas maneiras de fazer alguma coisa, prefira sempre a mais simples do que a mais complicada e você vai me agradecer depois. Yagni, sempre que você for fazer alguma coisa, pensa assim: "You aren gonna need it?
" Então, tipo, será que você precisa mesmo fazer aquilo ali? E se a resposta for não, ou se aquilo que você tá fazendo não for essencial para entregar a feature que você quer, não faça aquilo. Não se esqueça de separation of concerns, então justamente de você conseguir separar os arquivos e você não misturar tudo.
Isso vai facilitar muito na hora de dar manutenção. E por fim, tenta organizar os seus arquivos por feature e não por layer. Você vai conseguir fazer a sua LLM achar os problemas e implementar coisas novas com muito mais facilidade.
pelo menos a minha experiência, isso tem dado muito certo. Eu recomendo muito que vocês façam o mesmo também. E é isso, pessoal.
Se vocês quiserem que esses padrões sejam implementados automaticamente no código de vocês, uma dica que eu dou para vocês é usar o Epic, que é o nosso eh PM e Tech Lead Coding Assistant. O Epic vai ajudar vocês a justamente aplicarem todos esses conceitos de arquitetura eh por default, tá? Sem vocês terem que ficar lembrando disso o tempo todo.
E se você quiser testar ele, você pode testar ele gratuitamente clicando no link aqui que a gente vai deixar aqui embaixo, tá bom? Então é isso pessoal, até a próxima.