Simple Go exchange rate recommendation system

This application does two parallel HTTP calls to https://exchangeratesapi.io/ to:

  • retrieve the latest exchange rate for the base currency GBP.
  • retrieve the historic rate for GBP for the last week (from same day of the week as today, so it's seven days ago)

It then make a naive recommendation as to whether this is a good time to exchange money (buy, sell or do nothing).

As per https://exchangeratesapi.io/'s recommendations, the results are cached (in-memory, for now) to avoid multiple similar calls to the API.

Test it and see coverage

  • go test .

  • go test -coverprofile coverage.out and go tool cover -html=coverage.out

Build and deploy

To see the available command-line options:

  • build it with go build
  • run it with the -help tag

To deploy it as a single binary: copy the binary to an AWS EC2, execute it and enable it a service; or maybe put it as a service in a Hashicorp Nomad cluster; and so on.

To deploy it as a Docker container: make your CI/CD systems pull the code, build the binary, run docker build ., push the container into a remote container registry and then deploy it into an AWS ECS or some Kubernetes cluster.

Endpoints:

  • /recommend - the recommendation functionality
  • /metrics - Prometheus metrics
  • /healthz - liveness probe
  • /readyz - (mocked) readiness probe

Additional notes

The initial business requirements might have been somewhat different compared to what I implemented here. For example, as the time allocated for this was only 2 hours, I decided to instruct the exchange API to give me just GBP, with the EUR as its (default) base currency.

Also, I assumed that syntagm "last week historic rate" (rate is singular) was the rate for the same currency from seven days ago, as that's in the last week, same weekday as today.

Instead, my main focus was to provide a clean and "ready to be deployed" application. So if there's going to be a need for adjusting the business requirements, it can be done in a (very quick) next iteration.

I did not build it on a BDD or TDD manner, but decided to start writing the code first. It took me just about 2 hours to have the fully functional, clean code, but without tests. It might have been the migraine...

Then, after a break with some Coke and anti-headache drugs, I spent almost another 2 hours:

  • refactoring the code here and there (adjust variable and function names, split the code in files, etc)
  • abstracting the cache as an interface
  • writing tests (and mocking API's HTTP responses)
  • completing this documentation

The project is simple enough, so laying it out as go files in the root folder seemed like an acceptable solution, for now. For more complex projects, I use the standard Go layout

Regarding the API that the application exposes itself, I choose to respond with a simple text, a single human readable phrase. With Go, implementing some nice JSON responses for success and failure scenarios for an endpoint won't take more than a couple of minutes, so I thought this would be okay for this first iteration. Also, the initial requirements did not specify anything about the API interface.

Conclusions

Although writing Go code is a pure joy and I already have about 2.5 years of experience working with it on a daily basis myself, to build a production-ready clean microservice in just a couple of hours seem like a bit of a stretch. However, "production-ready" is to be perceived as a little bit heavier concept than "ready to be deployed" (as the first deployment can target only a private testing environment). Or isn't it? :)

All in all, this was a quite pleasant and gratifying experience. I'm once more thankful for the blessing of being a software engineer and even more for the opportunity to code in this very fun and powerful little language which is Go.