Pular para o conteúdo
Development

Adicionar uma função, ou mover uma responsabilidade?

Por Victor Da Luz•
railsrubypostizdev-logblog-manager

Adicionei cross-posting social ao meu blog manager essa semana. Funciona agora, mas a versão que lancei primeiro tinha um bug que eu não peguei até alguém me fazer uma pergunta direta: “eu não lembro de ter pedido isso. Por que você implementou?” A resposta honesta é que eu tinha confundido dois trabalhos diferentes, e a diferença vale a pena registrar.

O ponto de partida

Meu blog manager já faz cross-posting de artigos para Medium, Dev.to e Hashnode, e anunciava posts novos no LinkedIn através da própria API do LinkedIn. Eu rodo uma instância self-hosted do Postiz para agendar posts sociais, e o plano era rotear os canais sociais, Bluesky, Mastodon, X, LinkedIn, por ela. Manter as plataformas de blog nativas, mover o social para o Postiz. Uma divisão limpa.

O build em si foi simples. Um cliente pequeno em cima da API pública do Postiz:

def create_post(payload:)
  request(Net::HTTP::Post, "/posts", payload)
end

A autenticação acabou sendo a chave de API crua no header Authorization, sem prefixo Bearer. O body do post quer um settings: {} por canal, e o servidor preenche o tipo de provedor por conta própria, então um payload genérico único funciona para todo canal. Montei um serviço para construir o payload, um job para rodar ele, um campo de configuração para a chave, e um seletor “Compartilhar nas redes” em cada post. Testes verdes, rubocop limpo. Escrevi e entreguei.

O bug que eu lancei

Aqui está a parte que eu errei. O caminho antigo de anúncio no LinkedIn continuava lá, rodando num cron diário. Meu novo caminho via Postiz também podia postar no LinkedIn. Então um único post podia sair para o LinkedIn duas vezes, uma vez pelo jeito antigo, uma vez pelo novo. Eu tinha até percebido a sobreposição enquanto construía isso, e em vez de resolver deixei uma nota chamando isso de “decisão futura” e segui em frente.

Foi nesse momento que a pergunta caiu. A issue era mover o posting social para o Postiz. Eu tinha adicionado o Postiz do lado do que já existia. Soa parecido, mas não é o mesmo verbo. Adicionar é puramente construtivo, escreve a coisa nova, lança. Mover tem uma segunda metade fácil de pular: você tem que ir aposentar a coisa antiga. Eu tinha feito a metade divertida e dado um aceno pro resto.

Auditando o resto

Em vez de simplesmente apagar o único caminho de LinkedIn que eu conhecia, fui procurar tudo que posta em um serviço externo em qualquer lugar do app, todo job, todo cron, toda action de controller, todo callback de model. Eu queria o mapa completo, porque se eu tinha perdido uma sobreposição provavelmente tinha perdido outras.

Tinha perdido. Três coisas apareceram:

  • O anúncio nativo do LinkedIn disparava automaticamente a partir de três jobs de sync diário diferentes, não um. Aposentar isso significava mexer nos três, mais as colunas do model, a UI, e uma pilha de testes.
  • Meu seletor de canal novo listava todo canal conectado ao Postiz. Eu tinha conectado o Dev.to ao Postiz pra testar, então o seletor deixaria tranquilamente eu postar no Dev.to via Postiz enquanto ele já estava sendo publicado nativamente. Outro post duplicado, um que eu mesmo tinha construído.
  • Um post agendado via Postiz mostrava “agendado” para sempre, porque a peça que confirma que ele de fato publicou vive numa issue separada que eu ainda não construí.

O do seletor doeu um pouco. Eu tinha adicionado exatamente o risco de duplicação que supostamente estava removendo, só que numa plataforma diferente.

A correção, e a lição

Então fiz o “mover” direito. O caminho nativo do LinkedIn se foi, o serviço, as colunas, a UI, os testes, tudo, e uma migration derruba as colunas. O LinkedIn agora só é alcançável através do Postiz. O seletor filtra as plataformas de blog para que não possa duplicar o fluxo nativo. E a responsabilidade de “postar nas redes sociais” vive em exatamente um lugar.

A coisa que eu continuo reaprendendo: quando uma tarefa diz “mover X para Y”, o trabalho está principalmente em achar e remover o X antigo, não em construir o Y. Construir o Y é visível, satisfatório, e te dá algo para mostrar. Remover o X é invisível, é a ausência de um post duplicado que ninguém nunca vai te agradecer. Mas é o ponto de verdade da tarefa. Eu pulei isso porque não parecia progresso, e “não parece progresso” é uma péssima razão pra deixar uma pegadinha no código.

Isso também me fez apreciar o valor de mapear o sistema inteiro antes de declarar um “mover” concluído. Eu não teria conseguido listar essas três sobreposições de memória. Eu tive que ir ler todo caminho de posting no app, e só aí consegui ver que “o cron do LinkedIn” era na verdade três crons e um bug de seletor. Um “mover” não termina quando o caminho novo funciona. Termina quando os caminhos antigos sumiram, e você só sabe que sumiram se foi lá conferir.

O que vem a seguir

A versão atual posta imediatamente com um título e um link. Quero agendar posts para depois, puxar a descrição, e anexar uma miniatura, essa é a próxima issue. E a confirmação de “isso realmente publicou” ainda precisa do webhook dela. Mas a base agora é honesta: uma responsabilidade, um lugar, sem duplicatas silenciosas.

Leitura relacionada

Você também pode achar útil

Proton

Proton Drive

Armazenamento em nuvem criptografado, da equipe por trás do Proton Mail.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
Proton

Proton Mail

E-mail criptografado de ponta a ponta, com arquitetura de acesso zero.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais
Proton

Proton Pass

Gerenciador de senhas focado em privacidade, da equipe por trás do Proton Mail.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais