The article is also available in portuguese below!

In September, I decided that I wanted to develop a new project, with some fixed requirements:

  • It shouldn’t be centered around making any money
  • It should have Go as the main language
  • It should be a well structured project, even if it took longer to develop

My wife gave me the idea of making an application that allowed a user to subscribe to notifications of price reductions in Nintendo’s EShop Switch Games.

I added to the challenge by deciding the main interface should be a Telegram bot, since it would be able to send notifications easily and it would cut the cost of developing a graphical UI, which was not the point.

This was not my first project in golang. I had already tried making a port of Shlink, which was left unfinished due to some decisions I had made, resulting in the need of a major refactor. I wasn’t motivated enough to put in the work on the refactor and so that project is eternally on hold.

I had zero experience in web scrapping in golang, but the game data I needed from the web pages was simple and I got away with getting it with some regular expressions. In Python, I’d opt for using BeautifulSoup, which would further hinder performance of a great, albeit slow language.

For the database, I went with SQLite, since I wanted to build an app with the simplest deploy. Later I designed a MongoDB interface, to make use of Atlas, and learn some MongoDB in the process.

Sadly, midway through the project I realized I had made some important mistakes, regarding Nintendo Shop content. Since I had failed to identify a specific game ID in the page data, I used the eshop page link for that purpose. Later on I found that, not only the games are identified by a “nsUID”, but their current price is obtained from an API using that identifier. A big refactor to the core and database needs to be made… eventually.

I made a point to use a ports and adapters architecture, commonly known as hexagonal architecture. Apart from a few blog posts, I don’t have intimate (i.e. books) knowledge of it, therefore some mistakes may have been made in the implementation. There were some clear advantages to using this structure: it’s very simple to switch databases, since they behave like a plugin that is abstracted in the core logic; testing is also simpler, since mocking becomes a matter of satisfying and interface when the code is independent and not glued to one another.

When writing the docker file I was pleasantly surprised with the ridiculously small size of the “Scratch” image. The idea of deploying Go in docker is to have a multistage image, with a build and a deploy stage. The first one compiles the application and passes it on to the latter, which contains the bare minimum functionality required to run the program. I just needed to include the SSL certificates necessary to talk to websites using HTTPS. This experience was the direct opposite of the Python reality, where I maintain an image at work between 400-1000MB (depending on the use of python-slim and considering I’m using an Oracle library which is almost 100MB).

As for the refactoring, I’ll need to center the operation on the nsUID, since this is a much better identifier than the easily mutable website link. As it is, my app is a tiny refactor on the eShop away from total collapse. And two people can register the same game with different links, duplicating eShop requests, which I don’t like. Refactoring will include the core as well as the telegram and database plugins.

The lesson learned is that, unless you like to rewrite your code, you’d do well to make sure you understand your “business” and it’s logic. But also that, as sure as you are that you’re taking the right path, a refactor is always one “what if” away from someone looking at your problem with a different lens.

For those interested, the project is opensource and is on gitlab, here.


Em Setembro decidi que queria fazer um novo projeto de desenvolvimento, com algumas características fixas:

  • Não devia centrar-se em gerar dinheiro
  • A linguagem seria Go
  • Devia ser um projeto bem estruturado, à custa de rapidez de desenvolvimento.

Com o apoio da imaginação da minha esposa, o desafio tornou-se criar uma aplicação que permita subscrever a notificações de reduções de preço dos jogos da Switch da loja da Nintendo.

Adicionando ao desafio, decidimos que o interface da aplicação seria um bot do Telegram, visto que este teria a capacidade de enviar notificações facilmente e não necessitaria de um frontend gráfico.

Este não foi o meu primeiro proto-projeto em golang. O primeiro foi um port incompleto do Shlink, que ficou aquém e acabou no lixo por algumas decisões que fui tomando e resultariam na conclusão de que precisava de fazer um refactor importante. Como não tinha vontade de o fazer, está eternamente à espera.

A minha experiência em web scrapping era inexistente em golang, mas a simplicidade dos dados que necessitava culminaram na escrita de algumas regular expressions, suficientes para obter os dados dos jogos. Em Python, depressa optaria pelo BeautifulSoup, perdendo performance adicional.

Para a base de dados, escolhi SQLite, pois queria fazer a aplicação simples de distribuir. Mas mais tarde criei uma interface MongoDB, para tomar partido do Atlas.

Infelizmente, a meio do caminho percebi que tinha cometido alguns erros significativos, no que toca ao conteúdo da Nintendo Shop. Como não tinha identificado inicialmente nenhum ID específico dos jogos, acabei por usar o link das páginas na eshop para o efeito. Entretanto descobri que não só os jogos são identificados por um “nsUID”, como o preço atual dos mesmos é obtido pela consulta de uma API através desse identificador. Um refactoring importante da base de dados está planeado… eventualmente.

Tentei utilizar ao máximo uma arquitetura “ports and adapters”, normalmente conhecida como arquitetura hexagonal. Aparte de alguns blog posts, não li nenhum livro sobre o assunto, logo admito que possam existir erros na forma como implementei. Os pontos positivos claros foram: a simplicidade em trocar de uma base de dados para outra, pois esta comporta-se como um “plugin”, que o “core” tem abstraído; e a facilidade com que se pode testar as peças individualmente quando não estão todas coladas umas às outras, mas antes são montadas no início da aplicação, passando as structs aos receptores de interfaces.

Pelo caminho, fiquei agradavelmente surpreendido pelo ínfimo tamanho das imagens docker baseadas na “Scratch”. A ideia é utilizar um build stage e um deploy stage, no primeiro o executável é compilado e é passado para o segundo, que contém o mínimo indispensável para correr uma aplicação em Go, o que é muito pouco. Bastou adicionar os certificados para que a aplicação consiga ligar-se aos websites com HTTPS. A experiência é diametralmente oposta à realidade Python, com uma imagem de stage único que mantenho no emprego a pesar entre 400-1000MB (dependendo do uso do python-slim ou do python e tendo em consideração que contém uma biblioteca da Oracle de 100MB).

Quanto ao refactoring, será necessário centrar a operação do sistema em torno do nsUID, pois este não é tão facilmente mutável quando o link de um website. Da forma como está, todo o programa está à espera de um pequeno refactoring da eShop para deixar de funcionar. E duas pessoas podem registar o mesmo jogo com links ligeiramente diferentes, duplicando as chamadas à loja. O refactoring abrange o “core”, assim como os “plugins” do telegram e das bases de dados.

A lição aprendida é que convém ver e rever bem a “business logic”, a não ser que se goste de reescrever código. Mas também que, por mais seguros estejamos que vamos no caminho certo, um refactoring pode estar sempre à distância de um reparo por outra pessoa.

Para os interessados, o projeto é opensource e encontra-se no gitlab, aqui.