During the last months, I have being building an application called MoonCode to track my coding time, languages and projects I was working on. And let me visualize all that on a modern looking dashboard. In this article, I want to write about it, the features it currently has, how it works, why I built it and what I learned along the way.
Presentation
MoonCode is a platform tracking and giving you a summary of your coding activity: coding time, languages, files, all in one single place. To use it, you just need to install the VS Code extension, login, then everything is handled for you. Currently the project only supports VS Code as code editor.
Website link: mooncode.cc
MoonCode’s Features
- summary of your coding time, languages and projects per day, week, month, year, or any custom period
- support for most programming languages and files extensions. The extension automatically detects the language you are using
- the dashboard to visualize your data is local. It comes out of the box when you install the VS Code extension
- real-time sync dashboard (every minute). The stats always stay fresh and up to date with the data collected by the extension
- the extension works offline and synchronize to the API once you’re back online. So no data loss if you lose your internet connexion
- it also keeps your stats up to date automatically
Some screenshots of the dashboard and the extension:
Link on the VS Code marketplace if you want to try it: MoonCode VS Code extension. And the GitHub repository: MoonCode.
Architecture
The main parts of MoonCode are:
- a VS Code extension that collect all the relevant metrics (files data and programming languages)
- an API that manages all requests, built using NestJS. This choice was made because of the modular architecture of the framework
- a Vite+React Router dashboard that comes with the extension and allow you to visualize all the data in a user-friendly way
- a PostgreSQL database: a battle tested and robust option for an SQL database, used to store all the data needed: user and analytics data in one single place
- a Turborepo monorepo to keep all the parts and shared packages in a single, unified TypeScript codebase
- trpc between the API, the dashboard and the VS Code extension to allow end-to-end type-safety within the monorepo
- a website, built using NextJS. It is not really a core part, but more like the landing page of the project
So, in on excalidraw diagram it looks like this:
Why I wanted to build it
Why building a productivity coding time tracker when there are already plenty of them out there? Especially when there is already WakaTime doing the same thing, is more established, battle-tested and very feature-rich?
I found the free-tier of Wakatime a little bit limiting (as a cheap developer 😏). Don’t get me wrong, it is an awesome project and product and my goal is not to discredit it. I just decided to build my own, that does exactly what I need and give me full control over the data. About the data control, more on that later.
This project didn’t have to be feature complete, it didn’t have to be the perfect app, it just needed to solve that specific issue I had: track and monitor my coding statistics on any period I want.
And honestly this seemed as a very exciting side-project to work on. Because not only I would build something that could solve a problem I had, but it was very exciting to me personally. I already knew to build frontend web apps, but didn’t know enough about backend but the most interesting part was definitely the extension. How would it work and its life-cycle in general. I decided to focus only on VS Code for the moment because it is the code editor I use on the day to day.
What I learned/lessons
While building MoonCode I learned quite a few things about how VS Code extensions work, backend and APIs in general and Nest.js in particular. I also taught me some tips and tricks about TypeScript monorepos. I will try to be short here because some of the points deserve their own detailed blog post.
-
VS Code extensions: they were intimidating for me at the beginning, especially because not too much is said about them. We usually build frontend apps and backend systems, not vscode plugins. But the extensions are just Node.js applications with an entrypoint that is the
extension.js(built fromextension.ts) file, and it can be changed anyways. They have apackage.jsonlike any other Node.js app. The main difference with the other apps, is that you can use the VS Code API to create and execute VS Code commands, show notifications, subscribe to events happening in the code editor,… Two approaches are mostly used to build VS Code extensions: Functional Programming (FP) and Oriented Object Programming (OOP) approaches. I chose the FP approach since it is the one I was the most comfortable with. - Nest.js: before building MoonCode, I didn’t do a lot of backend, I had mostly worked with Next.js and even though it offers some backend functionalities, it is not a full fleshed backend framework. In my case the API I will build would be consumed by the extension itself, the dashboard and maybe other applications/packages later. So a separate backend was necessary. As I said earlier, I chose Nest.js for it scalability and opinionated nature (in fact, I wanted to try something new that was stricter, stable and battle-tested so I could really learn the backend side of applications). And I can say without any doubt that it is an, awesome framework, everything is organised in modules and properly separated. Even though Nest.js uses Oriented Object Programming, I didn’t need to be a master at it to be productive. The mental model was a little bit odd at the beginning but I started to click as I was using it.
- TypeScript monorepos and trpc: Turborepo is a simple yet very powerful tool to manage TypeScript monorepos, but it took me a very long time (many weeks/months) to really take advantages of its features: the cli, the cache,… I also used trpc to make everything typesafe from the backend to the frontend apps. It is so beautiful how the typesafety flows like water in the different applications. But it was very tricky to setup. I messed a lot with tsconfigs across the monorepo to get what I wanted. Some apps used ES Modules, others like Nest were still on CommonJS… It was a real chaos to make everybody share a typesafe contract, but it ended up being wired properly. What I also really like about monorepos is the ability to share common code/utility functions between all the apps in packages.
- Docker in monorepos: this one is also tricky but I can just have one single docker compose file with all my apps. Each of them has its own dockerfile. Or (this one is my favorite approach) deploy each app individually using its Dockerfile.
Building MoonCode was a real journey, I learned a lot. Some days it got very tricky due to the nature of what I was building (a VS Code extension was something new, but working with time in programming is, well, very tricky). But one thing I would 100% avoid if I had to start again is over-engineering. I wasted so many hours, days and even weeks trying to perfect the folder structure of the apps, get the ESLint rules right, …. It was useful in some cases like the VS Code extension and otherwise I would have ended with a real mess. But some days I felt like it was more like an excuse to not continue building, a fake productivity.
I still fall in that trap and I’m learning to detect when I start over-engineering and stop it if so. I have shipped the extension way earlier but I spent a lot of time fixing small issues, not getting anything really serious done for a long time. This is something I needed to hammer down in my head: avoid over-engineering at all costs. Perfection is the enemy, it kills momentum. Ship something that works. It doesn’t mean produce bad quality code, but just get it done. And move on with your day.
About the data ownership, one of initial goals of this project was to make it in a way to allow the user(s) to self-host the API and the database. This can be accomplished with Docker for sure. And since the dashboard is bundled with the VS Code extension, it doesn’t have to be self-hosted. The pain point I hit was to wire the VS Code extension and the API, so the user could update the API URL in the VS Code extension to their self-hosted version. I might find a good solution and ship it soon.
So, yes, that’s it. Thanks you if you made if so far in the article and happy coding !





Top comments (0)