While I've been a consumer of open source software for decades, I've often found it hard to contribute back to the communities. Until recently, all I've managed is the occasional typo fix in the documentation of whatever library.
Every so often I get the itch to contribute to upstream, but find it tricky. Godot is awesome, for instance, and I enjoy its C# compatibility. Then I read that it still has some parts missing, and I think - Great, I'll go pick up an issue! But then of course Godot is written in C++, and I'm completely out of my element there.
Or any other library or software is either in a foreign language, a tech-stack that makes me cry (JavaScript), or is just too deep and hard to fathom. For example, I've worked with Python for more than a decade, but every third party library follows their own very subjective conventions; organize the code in very peculiar ways; use a million odd analysis tools or package managers that no-one has ever heard of. Okay, to be fair, those last ones don't apply to Python as much as they do to JavaScript, but you get the point.
Kinda like gamedevs first want to build their own game engine before actually starting on the game itself; it seems that the JavaScript community is content with reinventing every technology all the way back to the steam engine before adding something innovative or useful.
Meanwhile, other developer communities seem to continuously build on the legacy of the past generations and come up with something useful. One individual who seems to be getting things right - in my opinion, of course - is Steve "Ardalis" Smith. While reading up on DDD, Clean architecture, DB mapper patterns, etc. Ardalis' name pops up a lot. He offers consulting, courses, a Blog, and also maintains Microsoft's eShopOnWeb sample application. One of his NuGet packages offers a C# implementation of the specification pattern alongside a slightly opinionated Repository implementation.
Generally, I was very happy to use this library and its Repository implementation. except for one detail: The Repository will call dbContext.SaveChanges()
after each "add", "update", or "delete" operation. While this will work just fine in some use cases, it becomes problematic when you want to work with the "Unit of Work" pattern. In this case, we want the Unit of Work to be in control of when/if we save the changes. In my projects, I use the Unit of Work pattern to abstract the concept of database transations, and use the Filter
part of Razor Pages to make sure that all requests are wrapped in a transaction. I do this so that I am able to rollback the transation in the event of an unhandled error so that I don't end up leaving "incomplete writes" in the DB. ARdalis' decision to have the repositories auto-save is because most requests will do some read and then a single logical write to a domain aggregate. While I agree with his point, my issue is that in the few cases where this is not true, we would need to do some custom handling and we would most certainly forget to do so until there is suddenly an error in production, and we start seeing "incomplete writes" in the database.
So I felt the need to go and see if I could maybe subclass the Repository class to override the auto-saving. This was possible, but became a bit clunky because the methods relied on fields, which are private, and therefor not reachable by the subclass.
So, now we're at the point of my story: I've made my first real open source contribution, and it was simply to turn the fields into properties and make them protected rather than private. Not much to brag about, but I enjoyed the exchange on Github and actually reaching people. You can check the conversation right here. It was nice to receive a code review and it even led to a short little learning for me about how field inheritance is probably a code smell. It is better to turn the fields into properties if they are to be made accessible to subclasses. This is because with properties, you can override the getter and setter methods, and thus have more control over what happens when the field is accessed or modified. More importantly, if the library needs to change in the future; this is the place where we have the chance to make the necessary adjustments to be backwards compatible and not break the subclasses.