Lessons learned creating a real-time multiplayer Tic-Tac-Toe game with Nun DB
A few months back, I wanted to leverage my career as a software engineer and get to the next level.
When I mentioned that to a good friend of mine in the industry, he introduced me to Mateus Freira and said that he could mentor me.
Mateus told me we could work together on an example project for this project he was building: Nun DB.
In exchange, I would have an excellent project in my portfolio, and he would go with me through all the nuance involved, namely all the best practices and any other tips he could give me all along the way.
In this article, I’ll explain all about the project and the process we went through.
First things first, What is Nun DB, and why should I care?
Well, Nun DB is an open-source real-time NoSQL database made to be fast, light, and easy to use. In addition, it is written to be memory-safe and horizontally scalable.
Mateus believes in keeping it simple and at the same time making it able to power lots of different apps and use cases.
You can check out Nun DB’s repository for more information and start using it in your project now!
By the way, I just realized as I was writing this that it’s called Nun DB because Freira, his last name, means nun in Portuguese.
Mateus’s proposal was for me to build a real-time multiplayer tic-tac-toe game using his Nun DB in conjunction with React so that he would have some excellent React examples for his database.
The first challenge was to build the domain model, a.k.a. all tic-tac-toe rules and an API to use and apply them.
I chose to use TDD (test-driven development) to help me in this quest since I believe it is the best approach to build clean code that can be safely refactored.
My first approach was to implement a class that would encompass all of the domain functions, i.e., filling a square, checking if there were a winner, etc.
I also wanted to make more than a simple 3x3 game, but to make it so that it’d be possible to play an NxN game as well.
It worked fine, and I used React hooks with it. But after I finished it and showed it to Mateus, he asked that I used Redux instead.
The reason for this change was that it would be much simpler to save the game states with Nun DB and guarantee that all players are on the same page by sharing it.
Here the challenges began because of the way I wrote my API. After all, it wasn’t simple enough that I could simply use Redux with it. So I thought a classless approach would be more fit.
Also, since I was using TDD, refactoring wasn’t that easy either because every API change would mean that I’d have to rewrite or refactor my tests as well.
Because of that, I never knew if a test was breaking because of a problem with the model I was writing or if the problem was the test. This was very annoying and time-consuming.
But when I finally managed to redo it, Mateus thought that many of the things I had done were too complex to understand and asked me to refactor many of the internal functions.
No problem, I thought. I have to do things more straightforward. Not a big deal.
Well, there was an issue, though: I was testing almost every internal function I had. Therefore, every change I made also meant I had to change a test, so the same problem described above occurred.
But in the end, after some arduous refactoring, I managed to create a model that pleased both Mateus and me.
Lessons learned from the domain modeling
Lesson learned #1:
Always think of a simple API that would be easy to use in any situation.
Lesson learned #2:
Avoid testing internal functions. The way things work internally doesn’t really matter as long as our API yields a correct result.
So If you have an API to solve a problem, focus on testing the public functions only because you might just want to redo how the whole thing works internally.
Lesson learned #3:
Even though I had some problems with how I used the tests, I think the whole experience would be much more painful if I had no tests at all.
So tests are definitely the way to go. Just be smart about how you use them.
We definitely had other challenges in this project, such as synchronizing the states throughout the web and creating multiple rooms.
But I think this article is already too long, so I’d rather write another article about those topics.
What would I do differently?
I would definitely be much more careful about the API I’m building from the get-go. So public functions should be very robust and consistent along the lifetime of your application and throughout all its versions.
Internal functions are not that big of a deal and may be refactored many times, so I wouldn’t really put much thought into them in the first version, and I’d let their ideal versions emerge with time.
Another important thing is that I’d try to understand Nun DB’s API before even starting the project. Because then I’d probably choose using Redux from the start, instead of having to refactor it during the project.
No matter how simple it is, every project may be an excellent way to learn new lessons, especially if you have to change demands and further information showing up during the development process.
Another good thing is to have someone else look at your code and see what can be improved.
Besides that, one should be very careful with the super important tool that tests are because they can also slow you down quite significantly if you don’t do them right.
Finally, the separation of the domain model in a public API and private functions is very useful to soften issues that might arise with your tests when you’re changing your code.