Spaced Repetition para Codebases: O Sistema que Te Faz Dominar Qualquer Projeto Legado em 7 Dias (Python + SQLite)
Você já herdou um projeto com 47 mil linhas de código, zero documentação, e o prazo de “entender tudo” era ontem? Eu já. E falhei miseravelmente nas primeiras três tentativas.
O erro não era falta de inteligência. Era falta de método. Eu tentava ler código como quem lê um romance — do início ao fim, esperando que a narrativa fizesse sentido. Spoiler: não faz. Codebases não são histórias lineares. São grafos semânticos com dependências cruzadas, e seu cérebro não foi projetado para absorver isso de uma vez.
A solução que mudou tudo? Spaced repetition — a mesma técnica que estudantes de medicina usam para memorizar anatomia, adaptada para dominar código legado. Não é mágica. É neurociência aplicada com Python e SQLite.
O Problema: Por Que Você Esquece 80% do Código que Lê
A curva do esquecimento de Ebbinghaus é implacável. Sem revisão, você perde 80% do que aprendeu em 48 horas. Isso vale para vocabulário alemão, fórmulas de química e — adivinhe — a arquitetura daquele microsserviço que você debugou na terça-feira.
Quando você abre um projeto legado pela primeira vez, seu cérebro faz o seguinte:
- Scan superficial: identifica padrões visuais (nomes de arquivos, estrutura de pastas)
- Busca de âncoras: tenta conectar o que vê com conhecimento prévio
- Sobrecarga cognitiva: desiste quando a quantidade de informação nova excede a capacidade de processamento
Resultado: você fecha o editor, abre o Twitter, e volta no dia seguinte tendo esquecido 90% do que “entendeu”.
A Ciência: Como Spaced Repetition Hackeia Sua Memória
Spaced repetition funciona porque respeita o timing biológico da consolidação de memória. Cada vez que você revisa informação no momento certo — logo antes de esquecê-la — a conexão sináptica se fortalece exponencialmente.
O algoritmo SM-2 (SuperMemo 2), criado por Piotr Woźniak em 1987, calcula intervalos ótimos de revisão baseado em:
- Qualidade da resposta: quão facilmente você lembrou (0-5)
- Ease factor: dificuldade intrínseca do item
- Intervalo anterior: tempo desde a última revisão
A fórmula é elegante:
def calculate_next_interval(quality, ease_factor, previous_interval):
if quality >= 3:
if previous_interval == 0:
next_interval = 1
elif previous_interval == 1:
next_interval = 6
else:
next_interval = round(previous_interval * ease_factor)
ease_factor = max(1.3, ease_factor + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02)))
else:
next_interval = 1
ease_factor = max(1.3, ease_factor - 0.2)
return next_interval, ease_factor

Adaptando SM-2 para Codebases: O Que Você Precisa Memorizar
Aqui está o pulo do gato: você não vai memorizar código linha por linha. Isso é inútil. O objetivo é construir um mapa mental funcional do projeto — entender responsabilidades, dependências e fluxos críticos.
Os “cards” do seu sistema de spaced repetition devem cobrir:
1. Módulos e Responsabilidades
card = {
"question": "Qual a responsabilidade do módulo invoice_reconciliation.py?",
"answer": "Compara invoices recebidas do Stripe com transações locais, detecta discrepancies e gera report diário.",
"file_path": "services/payments/invoice_reconciliation.py",
"dependencies": ["stripe_client", "transaction_logger", "report_generator"]
}
2. Fluxos Críticos
card = {
"question": "Trace o fluxo completo de um pedido desde o checkout até a fulfillment queue.",
"answer": "checkout_handler -> order_validator -> payment_processor -> inventory_reservation -> fulfillment_queue",
"entry_point": "routes/checkout.py:45",
"exit_point": "workers/fulfillment_consumer.py:12"
}
3. Decisões de Design
card = {
"question": "Por que o sistema usa event sourcing no módulo de inventory em vez de CRUD tradicional?",
"answer": "Precisa de audit trail completo para compliance SOX. Cada mudança de estoque gera evento imutável. Estado atual é projeção derivada.",
"related_files": ["events/inventory_events.py", "projections/stock_level.py"]
}
4. APIs e Contratos
card = {
"question": "Qual o contrato da API interna /internal/user/permissions?",
"answer": "POST {user_id, resource, action} -> {allowed: bool, reason: str?}. Rate limit: 100 req/min. Auth: service-to-service JWT.",
"defined_in": "api/internal/permissions.py:78"
}
5. Gotchas e Armadilhas
card = {
"question": "Por que NUNCA chamar cache.invalidate_all() em produção?",
"answer": "Faz flush síncrono de 2GB de cache Redis, causando thundering herd nos databases. Usa cache.invalidate_pattern() com TTL staggered.",
"incident_history": "INC-2022-0847, 3h downtime"
}
Implementação: Python + SQLite em 150 Linhas
Chega de teoria. Vamos ao código. O sistema completo cabe num arquivo Python e usa SQLite como storage — zero dependências externas.
import sqlite3
import json
from datetime import datetime, timedelta
from pathlib import Path
class CodebaseSpacedRepetition:
def __init__(self, db_path="codebase_sr.db"):
self.db_path = db_path
self._init_db()
def _init_db(self):
with sqlite3.connect(self.db_path) as conn:
conn.execute("""
CREATE TABLE IF NOT EXISTS cards (
id INTEGER PRIMARY KEY,
question TEXT NOT NULL,
answer TEXT NOT NULL,
file_path TEXT,
metadata TEXT,
ease_factor REAL DEFAULT 2.5,
interval_days INTEGER DEFAULT 0,
repetitions INTEGER DEFAULT 0,
next_review TEXT NOT NULL,
created_at TEXT NOT NULL
)
""")
def add_card(self, question, answer, file_path=None, metadata=None):
now = datetime.now()
with sqlite3.connect(self.db_path) as conn:
cursor = conn.execute(
"INSERT INTO cards (question, answer, file_path, metadata, next_review, created_at) VALUES (?, ?, ?, ?, ?, ?)",
(question, answer, file_path, json.dumps(metadata or {}), now.isoformat(), now.isoformat())
)
return cursor.lastrowid
def get_due_cards(self, limit=20):
now = datetime.now()
with sqlite3.connect(self.db_path) as conn:
conn.row_factory = sqlite3.Row
rows = conn.execute(
"SELECT * FROM cards WHERE next_review <= ? ORDER BY next_review ASC LIMIT ?",
(now.isoformat(), limit)
).fetchall()
return [dict(row) for row in rows]
def review_card(self, card_id, quality):
with sqlite3.connect(self.db_path) as conn:
card = conn.execute("SELECT * FROM cards WHERE id = ?", (card_id,)).fetchone()
ease = card[5]
interval = card[6]
reps = card[7]
if quality >= 3:
if reps == 0:
interval = 1
elif reps == 1:
interval = 6
else:
interval = round(interval * ease)
ease = max(1.3, ease + (0.1 - (5 - quality) * (0.08 + (5 - quality) * 0.02)))
reps += 1
else:
interval = 1
ease = max(1.3, ease - 0.2)
reps = 0
next_review = datetime.now() + timedelta(days=interval)
conn.execute(
"UPDATE cards SET ease_factor=?, interval_days=?, repetitions=?, next_review=? WHERE id=?",
(ease, interval, reps, next_review.isoformat(), card_id)
)

Workflow Diário: 20 Minutos que Mudam Tudo
Aqui está como usar o sistema na prática:
Dia 1-3: Extração de Cards
Abra o projeto e comece a criar cards. Não tente entender tudo. Foque em:
- Arquivos que aparecem em múltiplos imports
- Funções com nomes misteriosos (
process_data_v2_final.py) - Comentários com “TODO” ou “HACK” ou “FIX LATER”
- Configurações que parecem arbitrárias
Meta: 30-50 cards por dia. Leva 2-3 horas.
Dia 4-7: Revisão Intensiva
Agora o sistema entra em ação. Todo dia:
- Abra o script e rode
get_due_cards(limit=30) - Para cada card, tente responder em voz alta antes de ver a resposta
- Avalie honestamente: 0-2 (errei), 3 (acertei mas foi difícil), 4 (bom), 5 (fácil demais)
- Se errou, abra o arquivo relacionado e leia o contexto por 5 minutos
Tempo total: 20-30 minutos por dia.
# Script de revisão diária
from codebase_sr import CodebaseSpacedRepetition
sr = CodebaseSpacedRepetition()
due = sr.get_due_cards(limit=30)
print(f"Cards para revisar hoje: {len(due)}")
for i, card in enumerate(due, 1):
print(f"Card {i}/{len(due)}")
print(f"Pergunta: {card['question']}")
input("Pressione ENTER para ver a resposta...")
print(f"Resposta: {card['answer']}")
if card['file_path']:
print(f"Arquivo: {card['file_path']}")
quality = int(input("Qualidade (0-5): "))
sr.review_card(card['id'], quality)
print("Revisão completa!")
Automatizando a Extração: Parse Seu Projeto
Criar cards manualmente é tedioso. Vamos automatizar a extração de informações estruturais:
import ast
from pathlib import Path
def extract_module_cards(project_path):
cards = []
for py_file in Path(project_path).rglob("*.py"):
if "test" in str(py_file) or "__pycache__" in str(py_file):
continue
try:
with open(py_file) as f:
tree = ast.parse(f.read())
module_doc = ast.get_docstring(tree)
if module_doc and len(module_doc) > 20:
cards.append({
"question": f"Qual a responsabilidade de {py_file.name}?",
"answer": module_doc,
"file_path": str(py_file),
"metadata": {"auto_generated": True}
})
for node in ast.walk(tree):
if isinstance(node, (ast.ClassDef, ast.FunctionDef)):
if not node.name.startswith("_"):
doc = ast.get_docstring(node)
if doc:
cards.append({
"question": f"O que faz {node.name} em {py_file.name}?",
"answer": doc,
"file_path": str(py_file),
"metadata": {"auto_generated": True}
})
except SyntaxError:
continue
return cards
cards = extract_module_cards("/path/to/legacy/project")
print(f"Extraídos {len(cards)} cards automaticamente")
Métricas: Como Saber se Está Funcionando
Depois de 7 dias, você deve conseguir:
- Explicar a arquitetura em 2 minutos: desenhe o diagrama de componentes principais sem consultar código
- Responder “onde está X?” em 10 segundos: se alguém perguntar onde está a lógica de autenticação, você sabe o caminho
- Identificar 3 gotchas críticos: armadilhas que causariam bugs se você não soubesse
- Navegar sem grep: abrir arquivos diretamente pelo nome, sem buscar
Se não conseguir isso, ajuste o volume de cards ou a qualidade das perguntas. O sistema funciona — se você alimentar com informação certa.
Integração com Seu Workflow Existente
Não precisa abandonar suas ferramentas. Integre:
Git Hook: Revisão Automática Após Merge
#!/bin/bash
# .git/hooks/post-merge
python3 -c "
from codebase_sr import CodebaseSpacedRepetition
sr = CodebaseSpacedRepetition()
due = sr.get_due_cards(limit=5)
if due:
print(f'Você tem {len(due)} cards para revisar')
"
Slack Reminder
import requests
from codebase_sr import CodebaseSpacedRepetition
sr = CodebaseSpacedRepetition()
due_count = len(sr.get_due_cards(limit=100))
if due_count > 0:
requests.post(webhook_url, json={
"text": f"Hora de revisar a codebase! {due_count} cards esperando."
})
Quando NÃO Usar Spaced Repetition
Seja honesto: essa técnica não serve para tudo.
Não use quando:
- O projeto será descartado em menos de 2 semanas
- Você só precisa fazer uma correção pontual e sair
- A codebase tem menos de 500 linhas (leia direto)
- Você tem acesso ao autor original e pode fazer pair programming
Use quando:
- Você será responsável pelo projeto por 1+ mês
- Precisa fazer onboarding de outros devs depois
- O projeto tem múltiplas camadas e dependências cruzadas
- Documentação é inexistente ou desatualizada
Próximos Passos: Sua Codebase Espera
Aqui está o que você vai fazer agora:
- Baixe o código completo deste artigo
- Escolha um projeto legado que te assombra
- Passe 2 horas criando os primeiros 40 cards
- Revise por 7 dias consecutivos (20 min/dia)
- No dia 8, tente explicar a arquitetura para alguém
Se conseguir explicar sem gaguejar, você venceu. Se não, ajuste e continue.
Spaced repetition não é sexy. Não tem hype no Twitter. Não vai te dar estrela no GitHub. Mas vai te dar algo que 99% dos devs não têm: domínio real sobre código complexo, sem depender de sorte ou talento natural.
A melhor hora para começar a entender aquela codebase foi há 7 dias. A segunda melhor hora é agora.
Qual projeto legado você vai dominar primeiro? Me conta nos comentários — e se quiser ver um post sobre como gerar cards automaticamente a partir de AST de outras linguagens (TypeScript, Go, Rust), avisa aí que eu monto o parser.
Se curtiu essa abordagem de aprendizado acelerado, dá uma olhada na categoria Mente Binária — tem mais conteúdo sobre como hackear sua cognição com código.
