Bem-vindo ao xyzleo!
Este blog é um espaço para registrar meu aprendizado em programação e tudo que estiver relacionado a ele.
A ideia é usar o blog não apenas para publicar o que aprendi, mas também como uma forma de consolidar conhecimento, documentar projetos, falar sobre programação e acompanhar minha evolução como desenvolvedor de software.
Por que recomeçar do zero?
O LearningSea foi o primeiro projeto que fiz deploy em um servidor real. Com o tempo de uso, naturalmente comecei a identificar pontos que queria melhorar e features que gostaria de implementar.
Tá, mas por que recomeçar do zero em vez de simplesmente corrigir e implementar essas coisas direto no LearningSea?
A resposta vem abaixo, onde explico um pouco do motivo de ter escolhido Rails para esse novo projeto.
Por que trocar de Python/Django para Ruby/Rails?
Despertei um grande interesse por Rails depois de ouvir sobre o framework e sua filosofia pelo próprio criador, DHH. Também já tinha visto a famosa ideia de que Ruby foi criado com a filosofia de agradar o programador. Fiquei curioso sobre o que isso realmente significava na prática e quis experimentar por conta própria.
Além dessa curiosidade inicial, pensei que seria proveitoso criar o mesmo tipo de software — um blog web, usando uma arquitetura semelhante, e então poder perceber na prática as diferenças entre uma linguagem e um framework em relação aos que eu já conhecia.
Não é um port, é um recomeço deliberado
Antes de entrar no conteúdo técnico, vale deixar uma coisa clara: o blog xyzleo não é uma migração do LearningSea. Não portei models, não copiei lógica e não usei a arquitetura antiga como molde.
Embora o objetivo final dos dois blogs seja o mesmo, como dito acima, este é um projeto novo, construído do zero.
Stack
Comparando lado a lado:
| LearningSea | xyzleo | |
|---|---|---|
| Linguagem | Python | Ruby 3.4 |
| Framework | Django | Rails 8.1 |
| Banco de dados | PostgreSQL | PostgreSQL |
| Servidor de aplicação | Gunicorn | Puma |
| Reverse proxy | Caddy | Caddy |
| Frontend | Tailwind CSS | CSS puro (vanilla) |
| Cache / jobs em background | — | Solid Cache / Solid Queue (sem Redis) |
| Editor de conteúdo | Summernote (WYSIWYG) + Markdown | Markdown puro |
| Assets estáticos | WhiteNoise | Propshaft |
| JS | jQuery / plugin do Summernote | Vanilla JS + import maps (sem bundler, sem Node) |
| Markdown → HTML | python-markdown | Redcarpet + Rouge (syntax highlight) |
| Sanitização de conteúdo | Bleach | rails-html-sanitizer |
| Infra | Docker | Docker |
Os números
Rodando cloc no projeto, dá pra ter uma ideia real do tamanho do projeto hoje:
~5.200 linhas de código escritas em 136 arquivos — isso já excluindo todo o boilerplate padrão do Rails/Docker (scaffold não tocado, schema gerado, binstubs padrão) e qualquer arquivo .md de documentação.
| Categoria | Linhas | Arquivos | O quê |
|---|---|---|---|
Código do app (app/, lib/) | 3.301 | 86 | o blog em si |
| — CSS | 1.169 | 3 | application.css, admin.css, fonts.css |
| — Ruby | 987 | 31 | controllers, models, helpers, services |
| — ERB | 786 | 38 | views/partials |
| — JavaScript | 359 | 14 | interatividade leve (app/javascript/*) |
| Testes | 1.403 | 29 | tudo em test/ |
| Config/DB | 228 | 15 | rotas, importmap, CSP, migrations, seeds |
| Infra | 261 | 6 | Dockerfiles, compose, CI, entrypoint |
O que mudou de verdade
O que foi melhorado no novo blog e o que ele tem que o LearningSea não tinha (ou tinha de um jeito bem mais simples).
Escrita 100% Markdown, sem editor WYSIWYG. O LearningSea vivia num modelo híbrido: Summernote pra imagens e ajustes finos, Markdown pra escrever rápido — e os dois fluxos não eram simétricos (o HTML gerado pelo Summernote nunca virava Markdown de volta). No novo blog não existe mais Summernote: upload de imagem por drag-and-drop direto no textarea, inserindo
automaticamente no cursor. Um fluxo só, sem alternância de editor.Marginalia nativa em Markdown. As notas de rodapé (aquele span clicável com popup) agora são escritas direto no texto em markdown. Antes dependiam de um plugin do Summernote customizado por mim, que injetava no HTML a tag de marginalia. Além disso, agora existe um glossário reutilizável — defino um termo uma vez e ele pode ser referenciado em qualquer post futuro, sem reescrever a descrição ou inserir tudo manualmente.
Sistema de backup redesenhado. O do LearningSea versionava a cada salvamento (incluindo rascunhos), disparado por signals do Django. O novo é mais simples de propósito: só dois níveis — o "original" (primeira publicação) e o "atual" (sobrescrito a cada nova publicação) — disparado apenas quando um post é publicado, não a cada save. Menos histórico, mas exatamente o que eu de fato uso.
Temas, não só claro/escuro. Eram dois modos no LearningSea (claro/escuro, salvos no localStorage). Agora são 20 combinações: 8 temas completos (Dark, Light, Cream, Brown, Blue, Ruby, Purple, Pink), mais uma família "Contrast" estilo terminal CRT (7 variações) e uma família "Editor" com paletas reais de editor de código (Dracula, Nord, Gruvbox, Monokai, Solarized) — cada uma trocando só variáveis CSS, nunca a estrutura.
Tipografia selecionável. 4 fontes (Source Serif 4 como padrão, Inter, IBM Plex Sans, Source Sans 3) e 3 tamanhos de leitura, todas self-hosted.
Layout de três colunas fixo. Painel de marginalia (ou featured posts) à esquerda, conteúdo no centro, sumário (TOC) à direita — reservado em toda página, mesmo quando vazio, pro layout permanecer correto em qualquer interação. O sumário tem scroll-spy: destaca a seção atual enquanto você rola a página.
Posts em destaque. Um ou mais posts podem ser fixados na home. Não existia no LearningSea e dá um charme excelente, além de ocupar o espaço vazio que havia no lado esquerdo da página.
Admin 100% autoral. O Django Admin vinha pronto, de graça, junto com o framework. Aqui não tem gem de admin nenhuma — cada tela (posts, tags, marginalia, imagens) foi construída pra fazer exatamente o que eu preciso, sem herdar convenções de um admin genérico.
Páginas de erro no tema do site. 400/404/422/500 possuem um novo visual — a 404 até ecoa o caminho que você tentou acessar, no estilo de um terminal.
PT/EN continua igual. A ideia de colunas duplicadas (
title/title_en, etc.) em vez de uma tabela de traduções genérica já era a decisão certa no LearningSea, e eu simplesmente trouxe ela de novo: o idioma nunca vai passar de dois, então não faz sentido pagar a complexidade de um sistema pronto pra N idiomas.
Conclusão
Depois de usar por um tempo o LearningSea, percebi que eu precisava de muitas melhorias de vida:
- uma forma de inserir as marginalias de forma padronizada e automática
- backup de posts de forma granular ou em volume, diretamente pelo painel administrativo
- facilidade para gerenciar o CRUD de posts, tags, marginalias e imagens
- mais temas e opções de tipografia para o conforto do leitor
- um modelo padrão de escrita, que neste caso, foi o Markdown — sem necessidade de conversão extra ou trabalho adicional.
Além das melhorias de vida, conforme discorrido ao longo deste post, a organização e a robustez do projeto melhoraram, a quantidade de dependências diminuiu, o código ficou mais simples de manter e algumas responsabilidades passaram a estar melhor separadas. A própria estrutura e as convenções do Rails também contribuíram para tornar o projeto mais previsível e facilitar sua evolução.