I’ve had the ability to work on a number of exciting projects during my time as a developer, but the focus was always on delivering the most features in the least time. Though the professors at RPI did their best to show me how to write “good” code, once I got into a real agile development shop, I found that knowledge sorely lacking.
I decided to build a project during my down time explicitly to learn which techniques were most effective for creating software that’s easy to refactor and maintain. The project needed to be large enough to run into real world problems but not so large that I would never finish. I spent a good year studying the game of Go (also known as Baduk) and thought creating a multiplayer game server could be just the right level of complexity.
I began by writing a high level Design Principles document. This served as a loose requirement specification and let me get a sense of what features would be important to the end-user. Luckily I have a lot of experience with the problem domain, but if I did not, I would have started by getting user feedback before creating this document. It’s no use building something that does not solve the right problem!
“Pair Go for Teaching Games and Clear Progression”
From many long nights struggling to learn the game, I knew that climbing the ranks on your own in the game of Go is very difficult. I only began to improve when I found a teacher. But this took many small steps, from finding the player communities to building a reputation so a teacher would be willing to train me. This is a real problem that lacks a solution in the community.
So this project seeks to provide focused review and easy access to teachers for new players. To build this system, we need to explore the problem space and understand how best to deliver on this goal. I started the design process by creating a Feature Set that described the API in general terms.
To create a Feature Set, I created high level groups for each step of the user experience. For example, under the
teach section, I listed
teach live game and
review finished game.
At this stage the goal is not to nail down exactly what the end product looks like, but to begin understanding what actions a user would take and how they might traverse the site. This gave me a good sense of feature dependency issues and constrained the scope of the work.
It was clear there were a number of high level components that would need to work together to support the feature set.
- A database to hold game records and chat messages.
- A backend API service to deal with authentication and expose the database.
- A real-time service over WebSockets with a pub / sub model for live games.
- A client to call the other services and show data to the user.
Once I had these components in mind, I needed to decide what technology stack I would use to implement them. Over the last few months I have been spending most of my free time trying to learn how to use functional programming languages and Haskell in particular. Learning how to program without mutability has been super exciting and this project seemed to be the perfect opportunity to expand my knowledge.
More than just my personal excitement, Haskell was a great fit for this project because the type system makes refactoring a breeze. Selecting technologies that are conducive to large scale refactoring reduces the cost associated with iteration and lets me try new patterns without huge time investments.
Haskell is also at the forefront of programming language research. It offers powerful abstractions like type classes, Monads, and Type-Level API design. These tools are slowly making their way into more popular languages, and learning them from the source would help me understand how to use them in my day-to-day projects.
During my research into the Haskell ecosystem, I also found the excellent Reflex library. Reflex is a functional reactive library with a DOM management system in the style of React. It provides a very modern and manageable method for creating components that update according to state.
The other gem I found in Haskell is the Servant library. This library allows you to define your API once at the type level of Haskell, and then use that API to generate clients and request parsing automatically! This prevents a whole class of errors caused by the front and back end getting out of sync during the development process.
These libraries form the base of my technology stack, and taking the time to do research about the languages and ecosystems that are available saved me tons of time down the road.
After the technology stack had been fixed, the huge number of tasks became overwhelming. To maintain my sanity, I worked on the most isolated part first, the game logic. This let me work on a relatively contained part of the service so I could get more familiar with Haskell without dealing with hard problems like networking and IO.
Once I had the logic out of the way, I got started on the tough stuff, type level API and database design. In the next article, I’ll talk about those design choices and detail the cutting edge Haskell tricks I learned to use the backend API to generate the frontend client. Spoiler: the API is just a regular type!
If you are eager to see for yourself, take a look at the code!