Exportação CSV só resolve uma parte da saída de um SaaS: levar uma tabela para fora. Ela não comprova que os registros estão completos, que outra ferramenta interpreta os campos corretamente ou que anexos e permissões foram preservados. Antes de cancelar a conta, o teste útil é reconstruir uma pequena parte do trabalho no destino e conferir o que sobreviveu.
Um arquivo pode abrir sem mensagem de erro e ainda assim perder informação. O identificador 0007 pode virar 7; uma descrição com quebra de linha pode ser confundida com outro registro; um vínculo com cliente pode continuar como um código que ninguém sabe resolver. Aparência de planilha organizada não é evidência de migração correta.
Neste artigo
Defina o que precisa sair, não apenas o formato
Comece pelo uso que terá dos dados depois da troca. Para consultar uma lista antiga de tarefas, título, responsável e datas podem bastar. Para continuar operando uma carteira de clientes, talvez sejam indispensáveis histórico, relações entre tabelas, documentos e regras de acesso.
Essa diferença muda o aceite. Na avaliação de dependência de ferramentas SaaS, a possibilidade de saída é um critério de escolha. Na migração, ela precisa virar uma lista verificável:
| Item | O que conferir no exportado | O que testar no destino |
|---|---|---|
| Registros | Quantidade, filtros usados e IDs únicos | Nenhum registro desapareceu ou foi duplicado |
| Campos | Nomes, significado, valores vazios e tipos esperados | Datas, dinheiro e identificadores mantêm o sentido |
| Relações | Referências a cliente, projeto ou registro pai | O vínculo é reconstruído, não apenas exibido como texto |
| Arquivos | Documento incluído ou apenas URL | A cópia existe e abre sem depender da conta antiga |
| Operação | Histórico, responsáveis e permissões necessários | As tarefas essenciais ainda podem ser executadas |
Não pressuponha que o botão “exportar tudo” inclui tudo isso. A documentação do Notion, por exemplo, distingue exportação de páginas em Markdown e bases em CSV. É uma descrição de formatos e escopo daquele produto, não uma promessa de reconstituir qualquer espaço de trabalho em outro fornecedor. Não usamos uma conta do Notion neste teste.
O pequeno teste que encontra perdas silenciosas
Montamos três registros sintéticos com cinco problemas fáceis de esquecer: zero à esquerda, acento, vírgula dentro de texto, aspas e quebra de linha dentro de um campo. O arquivo fixture.csv tem este conteúdo:
id,cliente_id,descricao,valor,criado_em
0007,C01,"Café, suporte",12.30,2026-10-20T09:00:00-03:00
0012,C02,"Primeira linha
Segunda linha",7.50,2026-10-20T10:00:00-03:00
0105,C01,"Plano ""Equipe""",100.00,2026-10-20T11:00:00-03:00
São três registros de dados, embora existam cinco linhas físicas contando o cabeçalho. A segunda descrição ocupa duas linhas dentro das aspas. Por isso, contar quebras de linha ou usar split(",") não é uma maneira confiável de ler CSV.
A RFC 4180 documenta esse uso de aspas para campos com vírgulas, aspas ou quebras de linha. Ela é informativa: arquivos chamados CSV podem adotar outros delimitadores e convenções. O separador precisa ser conferido na origem e no importador, não adivinhado a partir da extensão.
Este código verifica a amostra com a biblioteca padrão do Python. Ele não acessa nenhuma conta ou serviço:
import csv
from decimal import Decimal
with open("fixture.csv", encoding="utf-8", newline="") as arquivo:
registros = list(csv.DictReader(arquivo))
assert len(registros) == 3
ids = [registro["id"] for registro in registros]
assert len(ids) == len(set(ids))
assert set(ids) == {"0007", "0012", "0105"}
assert all(r["cliente_id"] in {"C01", "C02"} for r in registros)
assert sum(Decimal(r["valor"]) for r in registros) == Decimal("119.80")
por_id = {r["id"]: r for r in registros}
assert por_id["0007"]["descricao"] == "Café, suporte"
assert por_id["0012"]["descricao"] == "Primeira linha\nSegunda linha"
assert por_id["0105"]["descricao"] == 'Plano "Equipe"'
assert por_id["0007"]["criado_em"] == "2026-10-20T09:00:00-03:00"
print("3 registros; IDs, textos, vínculos e total conferidos")
Na execução local com Python 3.14.2, a gravação e a leitura preservaram os três registros e todos os campos; a soma foi 119.80. Também submetemos cópias deliberadamente incorretas ao validador: registro faltando, ID repetido, zero à esquerda removido, cliente inexistente e delimitador errado. As cinco foram rejeitadas. Isso comprova o comportamento desta amostra e destes testes, não o exportador de um SaaS nem o importador de uma planilha.
Para uma exportação real, os valores esperados devem vir de uma referência independente do arquivo, como a contagem da origem no mesmo recorte e momento. Comparar o arquivo com uma contagem calculada a partir dele próprio não encontra registros que nunca foram exportados.
CSV não guarda sozinho o significado dos campos
Na leitura padrão, o módulo csv do Python entrega campos como strings. Isso preserva 0007, mas não declara que ele é um identificador. É o contrato de importação que deve manter esse tipo. Converter todo campo que parece numérico pode destruir códigos, CEPs e números de documento.
Dinheiro também exige uma decisão explícita: separador decimal, moeda e regra de arredondamento. O exemplo usa ponto decimal e Decimal; não mistura valores de moedas diferentes. Para datas, mantivemos horário e deslocamento -03:00. Uma aplicação que importa apenas a data, descarta o fuso ou interpreta dia e mês em outra ordem pode mudar o significado sem tornar o arquivo ilegível.
Há ainda uma diferença entre “campo vazio”, “valor desconhecido” e “não se aplica”. CSV, por si só, não define essa semântica. Documente a convenção antes de substituir vazios por zero, remover colunas ou preencher valores automaticamente.
Depois de importar no destino, exporte uma amostra de volta, quando possível, e compare IDs, campos e relações. Esse segundo percurso testa o destino. A leitura em Python é apenas o primeiro controle, não seu substituto.
O que não cabe na tabela precisa de outro caminho
Uma célula com o endereço de um contrato não é o contrato. Verifique se os arquivos podem ser baixados de forma autorizada, se suas referências continuam correspondendo aos registros e se o acesso não depende de uma sessão que será encerrada ao cancelar o serviço. Faça a conferência sem tornar documentos privados públicos.
Relacionamentos merecem o mesmo cuidado. Na amostra, C01 e C02 são referências a clientes conhecidos. Numa migração de verdade, será preciso exportar também o cadastro de clientes e reconciliar os identificadores, especialmente se o destino gerar IDs novos.
Permissões, comentários e trilhas de alteração podem ter exportação própria ou não ser portáveis. Registre a perda e seu impacto antes da decisão comercial. É a mesma distinção entre guardar uma cópia e ter uma restauração realmente testada.
Cuidado ao abrir dados de terceiros em uma planilha
Planilhas podem interpretar conteúdo de células como fórmulas. A OWASP descreve o risco de CSV Injection quando dados não confiáveis chegam a esses aplicativos. Colocar o campo entre aspas resolve a estrutura do CSV, mas não é uma proteção universal contra a execução de fórmulas pelo programa que o abre.
Use um fluxo de importação que permita revisar delimitador, codificação e tipos. Para conteúdo não confiável, prefira primeiro inspeção como texto ou com parser, sem avaliar fórmulas. A proteção precisa considerar o aplicativo de destino; não altere silenciosamente o arquivo original e depois o trate como uma cópia fiel.
Quando a exportação pode ser considerada aproveitável
O aceite deve responder cinco perguntas: todos os registros esperados saíram, os campos conservaram o significado, os relacionamentos foram reconstruídos, os arquivos continuam acessíveis e o trabalho essencial funciona fora da ferramenta?
Se uma delas ficar sem resposta, o próximo passo é corrigir ou aceitar conscientemente essa limitação, não cancelar a origem para descobrir depois. Um CSV que abre é um começo. Uma pequena migração conferida, com perdas registradas e critérios de comparação, é a evidência que faltava.