Moving Forward
If you’re following the progress of Duality, then you are already aware that I have cut back my role in the development of the project for personal reasons, and am now advising a group of core maintainers from the community who continue where I left off.
After almost ten years as the main developer of the project, this was a big leap of faith for me - but in retrospect, I probably should have taken it years ago. Let me explain.
The old days
When Duality started to happen in 2010, it was really just me, and some rough idea to generalize and expand on libraries and tooling that I developed for previous game projects. I took a lot of inspiration from my time as a student and intern at Deck13 as well, and also mixed in some Unity 3.x concepts for good measure.
One thing I learned from both was the concept of Entity Component Systems, which was entirely new to me at the time - probably one of the many reasons I never quite managed to build tech that outlived the single game it was powering. Every game I did before had its own engine and (sometimes elaborate) tooling, and I simply hadn’t figured out how not to re-do this all the time.
For the first few months, Duality wasn’t even on any version control system, and I’m not sure it had a name yet back then. I moved it to an SVN repository on Google Code soon after, where it remained until 2013. The project moved to GitHub after that, and everything that followed is somewhat well documented in git history.
But we’re rushing ahead already - the point is, Duality was a hobby project, a vessel for my own adventures in game development. Since I figured it could be useful to others and appreciated the feedback, I pursued it as an open source project, but it was essentially mine: Other were welcome to look at it, use it, copy it or discuss it, but I wouldn’t let anyone touch the codebase. It was a garden with visitors, not a co-op initiative.
A gradual shift in perspective
But over time, things changed. I started getting e-mails from people who found the project and wondered how one might actually use it. Support and feature requests started popping up in forum threads I scattered across various developer boards, or just messages stating “doesn’t work”. It wasn’t viable anymore to be the single point of entry for any Duality question, so I set up a community forum, moved to GitHub and started documenting progress and issues more publicly.
I started to care about people’s projects a lot, because if they put enough trust into my engine to base their games on it, I felt like I owed them to make it work as well as possible. And in a way, I still believe that to be true - but you can sort of see how this is not a viable long term approach for a project like this. I no longer saw Duality purely as a hobby project of mine, but also as a public entity that I (at least somewhat) owed my services to. It might sound a little dark when I phrase it that way, but since I loved working on that kind of tech, it was a great experience as well: Knowing it would make a difference to someone was a huge productivity boost for me, and having people around who offered critique and requests was a great opportunity to improve.
As more people started using Duality, some of them joined me along the way with bug reports, feature requests, design discussions and occasional pull requests. I started reviewing contributions alongside the regular maintenance work, and stopped seeing Duality as something that was exclusively mine.
And I really liked the idea of that! I’ve always enjoyed working in a team, and I enjoyed working on Duality, so combining both into one thing sounded pretty awesome. For some reason though I had no idea how to proceed and officially make it happen.
Running out of time
While there were many outside contributors, most of them didn’t stick around for very long. For those who did, there was always a layer of doubt: Could I really manage to transfer all the knowledge and context they needed? Ten years of accumulated source code is no joke. Would they take the project as seriously as I did? What if they broke it? What if they disagreed with the way I wanted to move the project forward? There was rarely a Pull Request with no changes from my side, so what if all those little issues ended up in the codebase unaltered? How could I trust anyone else to work on the project without my supervision?
It went on like this for a few years until it stopped for other reasons. I was used to working on Duality besides studying, having a job and generally wearing a lot of hats, but at some point changes to my personal life essentially zeroed out my available project time. Reducing my work on Duality to bugfixes and minimum support allowed me to continue for a while, but eventually, it stopped working. I was out.
Thankfully though, SirePi and Barsonax, two devs and long-term regulars from the community, offered to take over. And (spoiler alert) they’re doing a great job. None of my petty concerns about more independent and self-sustained contribution made any sense. So why the big fuzz?
Lessons learned
There was a hidden learning curve for me. Duality started as a personal project with publicly available source code, but as it grew to become an actual open source project, my mindset still lagged behind.
I wrote 99% of the source code, so “obviously” I knew what was best for it. I always tried to keep an open mind and consider suggestions seriously, but the proper solution was always something that I had to find myself, or at least integrate myself into the existing model of the project. While I was providing a clear direction and vision, I was also the single bottleneck. In other words, I took myself way too seriously.
This is reflected in how I reviewed Pull Requests: When I did my first reviews, I essentially asked contributors to write code my way. If something wasn’t like I would have done it myself, that was either a change request, or a post-merge edit by myself. After that, new additions and changes were indistinguishable from my own writing, because that was the standard I was expecting here - for the sake of consistency, code quality, and so on. It meant two things:
- That there was no risk of any fundamental impact in “how things were done around here”, so I could still treat the project like “mine” without worrying about being inconvenienced by different execution styles and goals.
- That there was a ton of work on my side for every PR. In some cases, they essentially became writing prompts.
Needless to say, this was terrible, and not at all how I believe open source, or really any big team project should be done. You cannot funnel a multi-person project through one mind, and you shouldn’t either.
If you would have asked me about that in 2013, I probably would have agreed, before continuing without fixing it. I simply didn’t realize what my approach at the time actually meant in practice, and which contribution dynamic it would create. Thankfully, I’ve had the chance to learn a bit from all the reviews since then and my personal take-away is: Relax a bit. Don’t judge too hard. Be open-minded. There’s standards and requirements, but there also needs to be wiggle room:
- Yes, code style is important, but also a footnote. We have a written style guideline now. Just point to it and don’t write a paragraph for every single violation.
- Yes, code design is important, but also too complex to assume you always know how it should be done. Talk about it. Keep it simple.
- Yes, consistency is important, but also a pipe dream. A codebase of ten years, even if written mostly by one person, is inconsistent to begin with. Make a reasonable effort, but don’t expect perfection.
- …and so on.
Life is messy, and so is a living project. Yes, there can be no stability if everything re-invents itself continuously - but there can also be no progress if everything is pressed into the rigid structure of a fossil. The goal needs to be a healthy compromise: A dance between the stable and the new.
A different aspect of this was the level of polishing I generally expected of any new contributions: Sometimes I rejected Pull Requests because they introduced a new feature or concept that wasn’t quite on the same level as the existing ones. My reasoning was that this could otherwise reduce the quality of the overall product, so in a way it would be better to not have it at all, than to have it in arguable quality.
From today’s perspective, I think this is mostly nonsense. Sure, there should be no regressions - but over-the-top requirements beyond that can be very harmful to the contributing community: They induce a state of over-fitted paralysis where a 90% hit is still a miss, and one or very few minds alone decide what is considered perfect.
Accepting the imperfect is vital, however. It invites progress and avoids subjective bias, and in the end, every new feature needs to start somewhere. As long as there are no regressions in a reasonable first step, why not have a little good faith and roll with it? A first iteration is not only more useful than no solution at all - it also provides a great platform for growth and improvement, on which both the project and its community can thrive on.
Closing words
Overall, I’m just super glad how this journey has turned out, both for Duality as a project and myself as a developer who had the chance to learn from its course. I hope that some of my thoughts did something useful for you - it certainly helped me to take this opportunity to look back.
And since we’re done with that now, I’d like to use that opportunity for a big shout-out to the new core team and their upcoming v4.0 milestone! There’s some gems in there already, and I’m really looking forward to how things will develop from here.