02Critério5 min

O que sempre acaba acontecendo quando começo algo na mão

Construí editores na mão quatro vezes. Três acabaram reescritos com uma biblioteca. A quarta não chegou a dois dias.

Por Salva Castells

Imagem de capa: Globetrotter19, CC BY-SA 4.0.
Neste post

Em setembro de 2024 passei três semanas construindo um editor de blocos. A ideia era a de sempre: cada parágrafo é um bloco, você arrasta, reordena, e cada bloco tem seu próprio botão de IA para reescrevê-lo. Nada exótico. O Notion faz isso há anos.

Montei com dnd-kit para o arrasto e uma instância de editor por bloco. O componente Block recebia id, index, content, onDelete, onContentChange, uma função regenerateContent com cinco parâmetros, a lista inteira de blocos para o contexto e um AbortController num ref para cortar a geração no meio. Funcionava. Um dos commits daquela semana se chama literalmente "never give up!!". Foi isso que ficou escrito.

Seis semanas depois tudo aquilo estava apagado e no lugar havia um único editor com extensões.

O que quebra quando é você quem orquestra os blocos

O problema não era o arrasto. O arrasto deu certo. O problema era todo o resto, e fui descobrindo nesta ordem:

  • Sincronizar conteúdo para dentro. Cada bloco tinha um useEffect comparando content !== editor.getHTML() para chamar setContent. Comparar HTML como string para decidir se o estado mudou é exatamente o tipo de coisa que parece funcionar até não funcionar.
  • Seleção entre blocos. Não existe. Se cada bloco é seu próprio editor, o usuário não consegue selecionar do meio de um parágrafo até o meio do seguinte. Não há conserto parcial: ou há um documento, ou há N documentos.
  • Desfazer. Cada instância tem sua própria pilha de histórico. Ctrl+Z desfaz no bloco onde está o cursor, não a última coisa que você fez. Ninguém espera isso.
  • Colar. Você cola três parágrafos num bloco e os três ficam dentro de um bloco. Separá-los é reimplementar na mão o que o esquema do documento já sabe fazer.

Nenhuma dessas quatro coisas estava na minha cabeça quando comecei. As quatro apareceram na mesma ordem em que as escrevi.

A reescrita foi mais curta que o protótipo

Em meados de outubro a pasta block/ já convivia com uma pasta block2/ que tinha três arquivos: o editor, a barra de ferramentas e um hook. O hook inteiro tinha quarenta linhas: StarterKit, TextStyle, FontFamily, TextAlign, Highlight, Placeholder. Seis extensões declaradas num array, e com isso vinham o histórico unificado, a seleção contínua e a colagem com esquema. Em novembro chegaram as listas de tarefas, os blocos de código e as imagens por URL: um import e uma entrada no array para cada uma.

O nome da pasta diz tudo. block2 não é um refactor. É reconhecer que o do lado não tem salvação.

O mesmo com o CSS deste site. Eu tinha um arquivo de mil seiscentas e oitenta e uma linhas com classes próprias. Ao passar para componentes, a única coisa que sobreviveu foram os tokens de cor: cento e treze linhas em globals.css. O resto era reimplementação.

Onde a biblioteca sempre ganha

Não é "use bibliotecas". É que há categorias concretas em que o problema é maior do que parece de fora, e são sempre as mesmas:

  • Editores de texto rico. Seleção, histórico, colagem e esquema são um só sistema. Não dá para pegar três de quatro.
  • Autenticação e sessões. Ninguém agradece o refresh token que você mesmo escreveu.
  • Datas e fusos horários. O caso raro chega em produção, não na sua máquina.
  • Estilos. Um sistema de classes próprio começa mais limpo e acaba sendo um framework pior documentado.

O sinal comum é este: se a parte difícil não é a primeira tela e sim a interação entre peças que você não vê no começo, a biblioteca ganha. Se a parte difícil é a sua lógica de negócio, não existe biblioteca.

Como decido hoje

Mudei a ordem. Agora começo pela biblioteca e só saio dali se ela me travar.

Um editor colaborativo de que precisei no verão passado foi resolvido assim: partir de um template do PlateJS e adicionar Yjs em cima. Três dias depois do primeiro commit já havia cursores remotos de outros usuários na tela. Meses antes eu teria começado pelo Block com cinco props. Para uma ferramenta de notas em Markdown no ano passado nem cogitei o editor: CodeMirror, um import de highlight.js no CSS, e tocar o barco.

E há um caso que vai ainda mais rápido. Em abril de 2025 escrevi um agente com ferramentas, um agent.ts com suas tools de contexto. No dia seguinte há um commit que o elimina e usa o streaming do provedor direto. Menos de vinte e quatro horas entre construir e apagar, e esse é o resultado bom.

O custo real

Construir na mão não é jogar tempo fora: eu sei o que a colagem faz num editor porque a reimplementei mal. Esse conhecimento não se compra lendo documentação.

O custo está em quando você descobre. Três semanas de blocos na mão e mais seis de convivência entre block/ e block2/ são dois meses. A primeira página de um mapa em 3D durou dez dias antes de um branch chamado map-v2 substituí-la inteira; ali o custo foram dez dias, não dois meses, porque cortei antes.

A diferença entre as duas não é quanto eu sabia ao começar. É quanto demorei para aceitar que já sabia o bastante.

Construir na mão é uma forma cara de ler a documentação. Às vezes vale a pena pagar; o que não vale a pena é pagar duas vezes pela mesma coisa.