Almost exactly three years ago, I wrote an announcement about a new side project I started working on: Doppelkopf, a browser-based version of the popular German card game with the same name.
I’ve been quiet about its progress ever since. It’s not like I’ve written lots of other stuff either, but I’ve also been quiet about that project in particular. You might be wondering about the project’s fate. Did I give up? Hit a roadblock? Nuke the entire Git repository by accident?
Nope, none of it! Almost 3 years and over 1024 commits later the project is still alive and kicking and, just like when I started out, it continues to be a lot of fun to build.
Hey you… If you’re just here looking for the Play the Game button: Here it is. But you’ve gotta come back and read on, you hear?
Go ahead, play the game right here ☝️
Contributions haven’t always been steady. Life happens. I changed jobs, moved to the countryside, got married, bought a house, got a fluffy coworker, baked at least a dozen really decent pizzas, wrestled a dinosaur, a lot of grown-up stuff. I’m trying to get my priorities straight and a browser-based card game surely doesn’t take the pole position in my life (sad, I know).
When daily life gets more calm, it’s my own meandering that gets in the way of steady progress. I like it that way since I’m partially this project to get my hands dirty with technology I’m curious about and to escape from the everyday chaos.
Sometimes I feel bad for not getting something usable out there sooner but then I remember that no one out there gives a damn because it’s not on anyone’s radar anyways.
I’ve meant to write an official update, an announcement of sorts, for at least 2 years now. Never wrote it. Mostly because I felt that the game just wasn’t ready yet. “Just these three more features, then we’ll write the announcement” I’d keep telling myself.
Yes, I know the The Lean Startup says that I should get stuff out while I’m still embarrassed or I’ve waited too long. My job at ThoughtWorks consisted of reminding people that releasing frequently and getting user feedback early is super-duper important. I’ve used lofty words like “MVP” unironically. All that only to ignore what I’d know better.
And you know what? It doesn’t matter. We’re not trying to be the Uber for niche card games or the Tinder for people who can hold 10 cards in their hand while drinking a beer with the other. There’s no rush, no deadlines, no pressure. Just a blissful journey towards creating an Open Source card game; and maybe one day someone has fun playing it.
Now that I’ve explained that slacking is cool when I do it, let’s move on and look at the things that have changed since I wrote the first few lines of code 3 years ago, shall we?
When I wrote the first blog post about this project, I didn’t have much more than a bunch of unit tests and primitive game logic in place. You could create cards, sort them, find out if one card beats another card. Low-level building blocks, only held together by unit tests.
Fast forward to today and we’ve got a playable single-player game in place, presented with a decent user interface. You can start a game, play multiple rounds, win a game, lose a game (the computer players are crap - you better win this, my friend!), get extra points for catching a Fox or winning a Doppelkopf and get correct scores after each round.
Yes, the experienced Doppelkopf aficionado will immediately notice that some things are still missing: You can’t play a solo. You can’t make any announcements. The computer players (think I should start calling them AI and score millions in venture capital?) make questionable moves. And yet it’s fun to play already.
The game’s got an interactive user interface powered by plain old HTML, CSS and Vue.js. When you build a browser game, one of the fundamental decisions to make is how you want to render your user interface. You get a few choices:
- use the good old DOM and reap the benefits that decades of web development have brought us, including CSS and all its clever layout technologies (
- render everything in Canvas if you favor low-level control and don’t want the comforts of CSS or need extra performance
- use WebGL if you want to get really fancy
- write everything in Flash and publish your game on newgrounds.com (just kidding, this isn’t 2003. Seriously, don’t do this!)
Doppelkopf is not a fast-paced 3D action shooter. We’re not rendering a lot of flashy animations nor do we care about high performance to let 4 players play one card turn by turn. On the contrary, we’re mostly dealing with rectangular shapes (cards), some icons and text that need to be moved around the virtual table neat and orderly. And this is where CSS and HTML are a great combo.
With Vue.js we can add a good amount of interactivity to handle simple interactions like “clicking on a card” or “pressing a button”. On top of that, its reactive programming model makes it really pleasant to represent the inner state of the game in the user interface without having to do any DOM manipulation by hand.
My Kryptonite! The game doesn’t look like ass, and that’s the most positive thing you’ll catch me say out loud. Somewhere deep down I know that the game’s visual design is okay - the color scheme is decent and doesn’t look like Hot Dog Stand, I’ve used some rather smooth CSS animations, and I’ve made sure to check some accessibility aspects that I know of. This isn’t meant to be fishing for compliments but when it comes to visual design, I’m incredibly picky yet unable to produce something great by myself.
This is why improving the visual design of the game is a massive time sink for me. I’ve spent days finding and tweaking colors on coolors.co and Color Hunt. I’ve searched, sketched, scribbled logos more than I can count, only to come up with… two overlapping cards? Well, at least it’s all CSS and has got a slick hover animation!
Whoop whoop! It moves!
I’m okay with where we’re at right now but I know that there’s so much room for improvement. I want to break out of the bleak and ordinary look of the game and make it a little more fun, a notch more quirky. I’d love to use illustrations, print stickers and card decks and send them out to players, contributors, or project sponsors - but I think I’ll need some help there.
This is a simple system. We’re not building a cloud-native, webscale, serverless event-driven microservice architecture (I really tried with the buzzwords here).
As much as anybody else I’m a big fan of keeping things simple. At the same time, I want to take this project as an opportunity to learn a bunch of new things. So we’ve gotta balance things to avoid falling off a cliff.
I’ve set up enough build pipelines and server infrastructure to know what it takes to get software out to production in an automated and repeatable way. At the same time, building servers, dealing with networking, watching build pipelines and provisioning scripts to finish and working on all the plumbing and duct tape required to combine these aspects is something that makes me consider quitting my developer life and moving to a farm at least three times a year.
For Doppelkopf’s infrastructure I wanted to find a pragmatic middle-ground. Something more modern than a hand-crafted snowflake server running in my mom’s basement. And certainly something less ridiculous than a full-fledged Kubernetes cluster running in a multi availability-zone setup with a global CDN in place. Let’s get some users first before we drown ourselves in infrastructure debt, shall we?
With all the noise and hype in our industry I found it incredibly hard to find the sweet spot and not fall for the siren sounds of cloud vendors and tool builders out there. Ultimately, I’ve ended up with this setup:
- Terraform is automatically spinning up the infrastructure - servers, domains, networking and very basic provisioning
- The Terraform configuration comes as an extra Git repository. Whenever I push changes there, a CircleCI pipeline will automatically build a new environment and deploy the application
- Everything’s running on a single instance of the cheapest server you can get on DigitalOcean
- The backend and frontend applications are built as Docker images and pushed to a Docker registry with every change (again, a CircleCI build pipeline kicking in)
- For deployment I
sshinto the target servers, pull both images from the Docker registry and start them with a simple
- Caddy takes care of setting up HTTPS using Let’s Encrypt and serving the applications
This setup is simple and effective. And most importantly: It’s fully automated. This way, I can work on infrastructure after not touching it for months without having to curse my past self for what they’ve done.
Yes, there are obvious shortcomings: no load balancing, no redundancy, no sophisticated content delivery networks involved. And you know what? At this point in time I couldn’t care less. All of these are the kind of problems to solve once you’ve got enough users playing your damn game. Before that happens, I’m not going to lose any sleep over my lack of redundancy for serving something’s that’s basically a silly static website.
boring mature tech stack
The road ahead
A lot has happened and the project has seen slow but steady progress. There’s still a lot of work to be done and I try to be transparent about the road ahead. I maintain a Product roadmap on GitHub for everyone to see. I’m excited about contributions of any kind (issue reports, documentation, design, feature ideas, code, you name it!) and I’m trying to make the project as welcoming as I can. If you want to join, be my guest. I’d be glad to have you!
Now that I finally got this heartbeat out in the open, I plan to write more regular updates with more detailed topics on specific aspects of the project in the upcoming weeks. Watch this space, check out the game and if you feel adventurous, come join me over on GitHub!