One of the mistakes I saw a lot of when Agile was taking off as “the hot-new thing” was people declaring that they didn’t need to do design anymore. Of course there was no big-upfront design - the kind from the waterfall days where people first analyzed and designed a system in full before punting it to the next team to build (which rarely worked out well) but some planning and design is important, even if you’re just creating a small hobby project.
Part of the process should often involve prototyping to help decide or prove which technologies you’re going to use as these will also factor in to the design of the app. It also gives you an opportunity to kick-the-tires of some technologies if they are things you haven’t used much before prior to building too many things around them. Making late-stage switches can be costly.
This isn’t going to be an exhaustive comparison of every possible client-side framework, storage technology or hosting option. Of course I have some technology choices in my head before I even begin based on my current skillset and experience (isn’t it an amazing coincidence that the “best” technology to build any app with is always the one the developer likes to use?!) But I’ll try to go through the reasoning behind some of the choices I came up with.
Firebase is great and makes serverless apps very easy to spin up but there’s always going to be times you need to access the Firebase data and / or user accounts from your server code (e.g. to sync accounts with a legacy system while we move to a Firebase auth enabled client).
There is an Official Admin SDK for Node.js, Java and Python but not all of them support user administration and poor Go doesn’t even get a mention (sad face).
But it turns out, we don’t need an official Firebase SDK for Go to be able to access the database and / or manage user accounts - all the packages to do it are already available and it’s very easy to use.
We all know that Go is extremely fast and very easy to develop with but as with any managed language, it’s easy to inadvertently generate large quantities of garbage. No, I’m not talking about poorly written code (yeah, I’m guilty of that!) but garbage as in “memory that has to be reclaimed”. This is done via Garbage Collection and the GC in Go keeps getting faster with each release but you still want to avoid it when you can.
One common culprit is creating temporary buffers for things like rendering, encoding and compression so an easy fix is to re-use these buffers instead of creating new ones each time. There’s a
sync.Pool in the standard library that can be used for this but there is a subtle “gotcha” which I’ll explain.
There are many challenges to understanding what our code is really doing at runtime:
We can’t always just attach a debugger and step through our code unless it’s running locally. But when it’s running locally, it’s not really representative of the live system. The only way to see what the live system is doing is to instrument it.
Fortunately, Google provide a fantastic tracing tool for their cloud platform which can provide valuable insights into your application even when calls span multiple services.
Here’s an example of using it to optimize code.