I’m not very good at chess, but I do like to play, and in the hopes of getting better, take notes on openings. I am not however able to understand the notation. I wanted to be able to look at my notes and instantly understand the position the notes where describing. like this
I had been playing around with Golang and wanted to learn more about framework distribution and serverless apis so though I would quickly build a tool which would enable my chess notes to be that much better.
Since then I have spent a considerable amount more building the tool then taking or reading the notes… such is life.
This document was written nearly a year after I built the tool, I now have much more experience and a better understanding of how things should be. This write up is a retrospective.
Problem
A user needs to be able to visualise any chess position as an image. This should be quick and convenient. As a side goal, the user wants to access the visualisations via external API for embedding on web applications or notes. Communication will be the sticking point. Effectively describing a full chess position in an intuitive way is challenging.
Product
This tools ultimately works in the way I needed it to at the time. However the user experience is rough and needs a lot of work. I’ll call out the major areas for improvement here.
“Chess-svg-api” does, for the most part, what it says on the tin. It programmatically creates SVG’s which display chess positions. It has a simply web UI, and a URL based API which enables SVG’s to be handled by web applications. To work with the product a user has to have a Fen string, which is a way to notate a chess position.
Technology
I wanted to play around with Golang as I knew it had a relatively simple syntax and could be very quick. I understood, at the time, that is was great for quick API’s. I knew that the service would need to be quick if it was going to be used to basically host images externally and display them programmatically as native content.
Stack
The service is built using Go Fiber and the image generation component of Notnil Chess which is a very comprehensive set of chess packages for Go. The product is very simple under the hood. Basically Fiber receives a request, validates it, calls the image generation function, and returns the results.
The UI is also handled with a Fiber call, the response is a Svelte built static output.
Issues
There where a few hiccups along the way. The largest of which is the way the package generates SVG’s by writing to a file. This is fine, and enabled me to do some fun optimisations by first checking to see if the file had already been created; building a poor mans cache. However it also made deployment a very difficult process. Having writeable files on edge servers is not really a thing. I tried Vercel edge functions and railway before getting things working on Fly.io I was able to get it working using Fly.io and docker.
The only extra wrinkly in this solution is dealing with pretty merciless cold starts. Meaning that if it is the first image generated in a while, it will take close to 4 second to create an image.
Testing
This was my first attempt at building anything backend related. I had selected a tech stack which was performance base so I wanted to understand the performance of the system. I used Apche JMeter; If I had the understanding I had today I would probably have gone for something like Postman. While I was using postman back then, I didn't think it had the volume capabilities which I though I needed. Turns out I didn't really. After building a few tests I was getting error rates in the 50% range. What I found interesting was that I would get similar failure rates for both 5 and 20 simultaneous users.
I discovered that I was basically using the file creating mechanism as a hard performance limit.
// create a file
f, err := os.Create("2.svg")
if err != nil {
c.SendString("Couldn't Create the File" + err.Error())
}
defer f.Close()
By changing the way the program was creating files I was able to open up parallelism and wildly increase performance.
name := strings.ReplaceAll(fen.Fen, "/", "")
// check if in system + if in system send it
if _, err := os.Stat("images/"+name); err == nil {
return c.SendFile("images/"+name)
}
// create a file
f, err := os.Create("images/"+ name + ".svg")
if err != nil {
c.SendString("Couldn't Create the File" + err.Error())
}
defer f.Close()
This change, discovered directly from the testing, took the system from 50% failure to a close to 0% failure on both 1, 5 and 20 users.
Fen String (and their issues)
Another challenge, and one which makes the user experience pretty much untenable, is Fen strings. Fen strings are a great way to notate a complete chess position. However they are nigh on impossible to generate without using a tool. This becomes an obvious issue when the user needs to track down a fen string generator to use my chess board image generator.
There is also an issue in validation. Fen strings have a few variants. Some notations provide more information then others. The process to validate a Fen string needs to check if the provided string can be used to generate a board not if it is a valid Fen. This leads to an infuriating interaction, where the user has gone to the trouble to find a valid Fen, but when they enter the string into the generator, or worst, the URL for the API, they are hit with the Error
Error handling in general was comically neglected. For instance this beauty;
// check if fen is in call
if err := c.QueryParser(fen); err != nil {
return c.SendString(“Oh no!”)
}
My understanding of error handling and its importance to good UX has come a long way since then.
Of course, the user screams, “Yes it is!?!?”. To fix this user experience issue would require a great deal more development of the validation code and the error handling. Which are both awful. The band-aid fix may be to provide a Fen as part of the UI to provide a reference, or even just some way for the user to understand which of the Fen variants they are able to use.
UI (or lack thereof)
The UI for this product was created as a secondary idea once the API was working. I wanted to explore svelte and better understand its novelties. The basic idea for the UI was that a user needs A) a place to put the input and B) a place to see the result. There are of course a laundry list of features which would make even this very simple interaction usable, none of which currently exist.
- Possible example fens.
- Playable board to generate fens.
- Some for of download or save functionality so the user could do something with the result.
- Basic history so the user could go back without having to generate the fen again.
- Any form of documentation.
I think, unfortunately the UI on this project is severally lacking and would be the first place to begin working on a V2.
Process
The process for this project was mostly one of technical design. Understanding how the technical decisions I had made would impact the user. The process was first to write down the tool I needed, work out the technology stack I could use and then get it working. I found that much of the improvement that was made to the product overall was made after the initial implementation. Most of the issues I worked on could have solve with a more thoughtful technology design.
Key Take Aways
- I leant a lot about technical implementations, API infrastructure and software deployments.
- Testing is an incredibly important part of iterating a product, not just functional testing but programmatic testing.
- Technical decisions have a huge impact on the end user experience.
- Features do not exist in a vacuum. A great feature may not be so great if the surrounding experience and features are not up to scratch (or don’t exist at all)
- Not all products have UI’s, this does not mean they don’t have user experience.
- Go is super fun
Next Steps
- This project was a fun build and a technology playground. If I was to revisit it I would need to do a tear down rebuild into a V2.
- V2 could start with a UI overhaul and the addition of many handy features, would also be great to continue exploring Svelte
- I probably need to have more chess notes to make the project viable at all.