Looking back at some of my previous personal projects on Github, it made me realize how much my coding style has actually changed since then.

One of my previous personal projects, the Simple Poll Engine which lets you quickly integrate a poll on your website, was written 5 years ago. Looking back, seeing that I’ve declared it “simple”, it’s kind of ridiculous how many files are actually contained in this project. This might have been a reason why the project never took off (4 stars, 2 forks and 1 open issue over the entire lifetime). Given my experience in the past 5 years after I’ve created this project, here I describe 2 problems that I used to create which I’m now actively trying to avoid.

Too Many Files!

First, to really deserve the name simple, this should have been a single file application that you can upload to wherever you want and start using it. Instead, in this project, I’ve counted a total of 24 PHP files. These files range from endpoint files like index.php, files that you can require from your own PHP script external/include.php, configuration file, HTML template files, all the way to stupid files like interfaces/database.php. These all need to be uploaded to the web server, and if I’m a user I’d be quite confused as to what all these files do. This project does provide an admin interface to manage the polls you create, but this certainly still looks overwhelming and it’s definitely not impossible to inculde that feature in a single file application.

This highlights one of the changes in my coding styles over the past few years. Previously, I might have been too obsessed on splitting files and keeping files small. Over the years I’ve learned that there’s actually no reason I need to split code into multiple files, especially in PHP. The only times I can think of a reason to split files, is from the cognitive convenience standpoint, where you know what the code in this file does, and if you have multiple entry point files you might want to put common code into a separate file just so you can include it from different files. As a simple example, you might have a file called database.php which handles all database specific operations, that makes sense. Another case is when you have index.php and install.php that both requires function abc, then it makes sense to put function abc in common.php, then both index.php and install.php can include that. The over-obsession shows in my Simple Poll Engine, when there are separate controllers.php, domain.php and usecases.php files. These all could have gone into one single common.php file.

Stupid Abstractions

Now this leads to another question, why do controllers.php, domain.php and usecases.php exist in the first place? This brings up another change in my coding style. Back then, I was quite obsessed with the Clean Architecture being advocated by Robert C. Martin. The idea that you have a layered architecture code where each layers are cleanly separated and are interchange-able between layers was kind of the craze back then. My mindset back then, was that code is very difficult to write and difficult to change, thus once code was written you want to avoid changing it as much as possible. This means that I was looking for a method of coding which allows me to first write a bunch of very very basic code, which will not change overtime, and then slowly build things up on top of that. The problem that this induced was that most of the time, I didn’t know very clearly what I was trying to write. And I was so obsessed at trying to avoid changing code that was written when I had no idea about how that code should be written! In hindsight this should have been so obviously ridiculous, but to this day I still work with developers that have the same mindset as the young me, and they think it’s righteous that they should write code this way.

Since this is a mindset that most developers still maintain which I feel is one that I’ve thrown away to grow up as a better developer, I’ll spend a little more text to try to explain this. Looking back at the project that I wrote as an example, PollRepository interface. An interface, in case you don’t write in PHP, is sort of an abstract class that defines what methods a class that implements this interface should have. The goal of this one PollRepository is to define a list of methods that should allow me to retrieve polls from whatever datastore there is. This project only supports one datastore, which is MySQL. This already gives one huge red flag, why do you need an interface when there is only one such class that implements it??? If you attempt to add another datastore that is significantly different from MySQL, for example a NoSQL database like mongodb, some operations that are cheap in MySQL and can be performed in one-go might be very expensive in mongodb!

As a totally arbitrary and uninformed example, assume that GetAnswersById is a very cheap operation in MySQL, since it’s cheap it’s called several times for every page load. Now I move on to implement mongodb support and GetAnswersById turns out to be a very expensive operation that requires several scans in mongodb, all of a sudden the performance of the application sucks. It turns out, I should change the structure on the code one level higher to not call GetAnswersById so often and instead use some other operations that are cheaper in mongodb and will produce the same result. Now how will I go to fix this issue? The code on the higher level will now have to be aware whether we’re using MySQL or mongodb and then choose which operations to perform depending on that, which defeats the entire purpose of this PollRepository trying to abstract away the datastore that is being used.

Now this is just one example where abstractions break down completely. In this case, it’s not only broken but it also actively stops you from making changes to lead to better code. I certainly don’t think that abstractions are evil and that we should be running away from abstractions completely. If you look at the above example, the abstraction was broken because it was constructed from just one concrete usecase. If you start off without abstraction, implemented two or three more usecases, then try to build an abstraction, that abstraction that you built is certainly going to be more useful than the one I’ve managed to build here. Note that in this case it can still fail if the usecases were one-sided (i.e. only relational database usecases and now all of a sudden you need NoSQL). So my coding style has now morphed into one where I delay building abstractions as much as possible. No abstractions until I feel confident that I can now create an abstraction that will help me. This situation where I start feeling confident enough to build an abstraction is somewhat arbitrary and relies more on my experience than anything, but I certainly do hope to be able to concretely lay down some requirements that I look for in the future.

Going Forward

5 years is a long time for someone to grow up. I’m fortunate that I can look at my code 5 years later and point out problems in it. Signifying that I have actually made progress in my personal development over the years and have not stopped learning. I certainly do hope that whatever code that I’m writing today, eventhough given my current knowledge I think it’s the best that I’ve ever written, I will be able to look back 5 years later and point out more problems.