Around 10 years ago, when I had just picked up coding as a hobby, I taught myself PHP (I was young). Using PHP and a few nights after school, I made a blog engine and never updated it since.
The engine was named t.t.t, an acronym for my old blog,
the.tslimi.tk. Apparently, it has lost this origin as I moved on to other blogging platforms. Feel free to interpret it any way you like.
t.t.t reads a directory of markdown files as posts. It doesn't use database simply because I didn't know how to use one. Although there was an experimental composing interface, it was not access-controlled, and you mainly post a article by uploading a new file. The PHP code renders each
txt file into
HTML and saves the HTML version in a
cache/ folder, where the code would look up first.
Over this holiday weekend, I was learning about containerization. As an exercise, I thought it would be cool to containerize a project of my own. The project of choice should be small in size so that it's manageable and won't dwarf the containerization part, and it should ideally be a web service. Naturally, I thought of t.t.t, which only had <1,000 lines of code and is a server.
While I was inspecting the code, I identified some low-hanging fruits that I could probably smooth out before getting into business, such as applying early returns and prettifying the code. The cleaning job, however, took me 10.5 work hours.
t.t.t used to have all its dependencies saved as part of the source code. This brought surprising longevity to the project: I downloaded the code from GitHub, executed
php in the decompressed directory, and it just works. Compare it with the last blog engine I wrote 5 years ago, GitPub: GitPub used external services for packages, namely Eager. As the company was acquired by CloudFlare, GitPub had long stopped working years since. Now I see the benefit of using a monorepo.
Back then, I didn't use any package manager simply because I didn't know such technology existed. A package manager helps your repo to contain only the code you write. This has benefits in terms of storage space and upgradability. Naturally, the first update I made to t.t.t was migrating all its dependencies to a package manager. In the case of PHP, I had to learn about a new tool, namely Composer.
Now that I have a package manager, the next thing I did was replacing some functionalities that I wrote myself with battle-tested packages available online. These include:
Pagerfanta. This saved
t.t.t ~150 lines of code, which is substantial considering the whole repo contained only ~450 LoC when I made this switch.
Instead of using one
favicon.ico file of
mspaint.exe quality, the blog engine is now using a whole set of favicon files generated from favicon.io.
Due to the switch in CSS, there are a couple of features that no longer fitted into the new design: The semi-transparent header images in the index page, and the "Quick Access" dropdown menu. I had to remove these features. Their removal led to further reductions in the code: t.t.t no longer hides files whose names begin with
_, and it no longer depends on jQuery (hooray).
It [felt good][fg] to see that the code complexity dropped when I was removing the header image feature. Therefore, I continued to remove more:
_intro.txt behavior has been removed to reduce complexity in the rendering process. This means t.t.t no longer hides
The caching behavior has been removed. It was a fun experience to have implemented my own caching mechanism, but the complexity-efficiency trade-off was just not paying off.
In addition (or, should I say "reduction"?), undocumented behavior (such as RSS support and an alternative list view) and dependencies that stopped working themselves (such as the social sharing buttons) were also removed.
Also, since it's 2020, containerization is great:
Heroku Procfile (and one-click deploy button)
It might sound like an overkill for a 300-LoC project, but isn't that the whole point of revising t.t.t?
These things sounds quite interesting:
add unit tests.
social-links/social-links as a replacement for JiaThis.
make the docker/kubernetes setup work with a volume mapping.