<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Théo Verdiere</title>
    <description>The latest articles on DEV Community by Théo Verdiere (@c0rbz_tm).</description>
    <link>https://dev.to/c0rbz_tm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3928532%2Fc47c3f65-5d24-4c4b-8cd5-f81ef4d03880.png</url>
      <title>DEV Community: Théo Verdiere</title>
      <link>https://dev.to/c0rbz_tm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/c0rbz_tm"/>
    <language>en</language>
    <item>
      <title>Yet another personal project. I over-engineered it on purpose</title>
      <dc:creator>Théo Verdiere</dc:creator>
      <pubDate>Tue, 06 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/c0rbz_tm/yet-another-personal-project-i-over-engineered-it-on-purpose-jkd</link>
      <guid>https://dev.to/c0rbz_tm/yet-another-personal-project-i-over-engineered-it-on-purpose-jkd</guid>
      <description>&lt;p&gt;It started one evening, out of the ordinary friction of the job: the organizational and budgetary constraints that come with any system at scale, and never enough time to keep things clean and pleasant to work on.&lt;/p&gt;

&lt;p&gt;Not a startup idea, not a side hustle with a monetization plan. Just a developer who'd spent a long time working inside systems he couldn't reshape, opening a fresh Kotlin project at 9pm because he wanted somewhere those limits didn't apply.&lt;/p&gt;

&lt;p&gt;So yes, this is &lt;em&gt;yet another personal project&lt;/em&gt;. The internet has no shortage of them. I knew from the start it would be oversized: a backend, an Android app, CI, infrastructure, a deployment. All for an audience of maybe six people. A bit of a champagne app on a beer budget.&lt;/p&gt;

&lt;p&gt;The overkill wasn't the surprise; that part was deliberate. What I didn't plan was building a &lt;em&gt;whole product&lt;/em&gt;. I started out just wanting to set up the infrastructure side myself, with something concrete running on it: a real frontend, a real backend, a real server.&lt;/p&gt;

&lt;p&gt;By day I work on systems that serve tens of thousands of concurrent users, and I'd only ever seen most of that machinery from inside a job. I wanted to take it apart myself, in close-to-production conditions, on something that was mine.&lt;/p&gt;

&lt;p&gt;It's been going for about 22 months now. What it became, and why I kept reaching for things it never needed, is the rest of this post.&lt;/p&gt;




&lt;h2&gt;
  
  
  The product, and the reason
&lt;/h2&gt;

&lt;p&gt;Before going further, here's what all that infrastructure serves.&lt;/p&gt;

&lt;p&gt;Grimoire is a place to keep your recipes and how-to procedures, which it calls &lt;em&gt;Spells&lt;/em&gt;. Your Grimoire is private: the stuff you reach for every day. That's the part that's live today, Android-only, on the &lt;a href="https://play.google.com/store/apps/details?id=com.corbz.grimoire.android" rel="noopener noreferrer"&gt;Play Store&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's wrapped in a cozy fantasy theme: Spells, a Grimoire, an Academy. I wanted it a bit geeky so it wouldn't feel like more work at 9pm. It's mobile-first (you cook, hands dirty, the phone's already on the counter) and deliberately slow: no feed, no nudges. You don't fill a Grimoire in a day.&lt;/p&gt;

&lt;p&gt;The rest is still being built. The Academy, a shared space where Spells get published, discovered and adapted: pull one into your own book, tweak it, share it back. A "lineage" view of how a published Spell gets forked downstream. Quests, for one-shot tasks. Guilds, smaller closed spaces to share within a group.&lt;/p&gt;

&lt;p&gt;That's the product. Most of it could have shipped on a single rented box. The app needed some of what follows; most of it I built because I wanted each layer in my own hands rather than borrowed from a job.&lt;/p&gt;




&lt;h2&gt;
  
  
  The thesis
&lt;/h2&gt;

&lt;p&gt;A personal project with no deadline and no boss is the easiest place in the world to over-engineer. I've come to think that's not a flaw. It's the point.&lt;/p&gt;

&lt;p&gt;At work you ship the minimum: someone's paying, and it's not your time to gamble with. Strip that away and you can afford to try things and see what happens, with nothing lost if they don't pan out.&lt;/p&gt;

&lt;p&gt;So I did the unreasonable things on purpose. Hexagonal architecture, which later let me swap search engines in a weekend. Zero-downtime deploys on an Elasticsearch I didn't need yet. A pinch of DDD while the app was young enough to absorb it.&lt;/p&gt;

&lt;p&gt;You only learn what a pattern is worth once you've lived with it, even with an understanding as humble, and sometimes as shaky, as mine. What hexagonal and DDD actually looked like in this codebase is its own post.&lt;/p&gt;

&lt;p&gt;None of this is advice: having no users and no revenue let me make trade-offs I couldn't defend at work.&lt;/p&gt;




&lt;h2&gt;
  
  
  Growing it one plateau at a time
&lt;/h2&gt;

&lt;p&gt;I never planned this as a stack. It grew in plateaus, each opened only once the previous one felt solid, and usually because I'd hit a wall that made the next layer worth building.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Felxo88c0b8rgujek65f0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Felxo88c0b8rgujek65f0.png" alt="Architecture overview of Grimoire: Android app and web admin, backend, datastores, k3s on a Hetzner VPS" width="799" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend, alone, for three months.&lt;/strong&gt; December 2024 to March 2025: just Kotlin and Spring Boot. I used the time to sit properly with hexagonal architecture and a contract-first OpenAPI spec. No client yet, just the shape of the system.&lt;/p&gt;

&lt;p&gt;It was also where I really learned Kotlin and Gradle, and came to like how little Kotlin asks of you to get something clean working. I made my share of design mistakes in there too; some I only saw later, once a second layer started leaning on them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then it got a face.&lt;/strong&gt; March 2025: Android with Jetpack Compose, built alongside the backend. Up to then the app had lived as a wall of requests in Insomnia; this is where it stopped being an abstraction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A pipeline, earlier than I needed one.&lt;/strong&gt; Around the same time, Dagger.io for CI: pipelines as real code, not YAML. Over-built in places, no question. But never wasted: it's what I run every day now, without thinking about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure, in a sandbox first.&lt;/strong&gt; Summer 2025: Pulumi, but on a local KinD cluster, not a live server. The same principle I lean on everywhere: the local environment should look as much like the real one as possible. So I got the deployment working locally before trusting it anywhere real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finally, a real server.&lt;/strong&gt; August 28th, 2025: a Hetzner VPS running k3s. I did this the old way, on a deliberately bare box: cheap, and empty. Everything a managed platform hides was now mine to stand up by hand: reverse proxy, cert manager, the lot. The point wasn't hardship for its own sake. A managed control plane lets you stay theoretical about that whole sysadmin layer, and I wanted to actually meet it. (It did break, spectacularly, eight months later. The post-mortem gets its own write-up.)&lt;/p&gt;

&lt;p&gt;From there it kept growing, driven by friction rather than a roadmap: Elasticsearch eating my VPS pushed me to Meilisearch; and since Spells are meant to be shared publicly, I built a small React admin to moderate them before opening that up.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where it didn't go to plan
&lt;/h2&gt;

&lt;p&gt;Deliberate overkill was only half of it. Plenty of the project came from the other direction: plans that met reality and bent.&lt;/p&gt;

&lt;p&gt;I set up a full Karate E2E suite early, expecting to lean on it; later I admitted it was the wrong call for a project this size and tore it out. I ran three separate repos for a year, sure it was cleaner, until the workflow got painful enough to push me into a monorepo. Neither was a master plan. Both were course corrections, one from hindsight, the other from sheer fatigue. (The monorepo move gets a full write-up later in the series.)&lt;/p&gt;

&lt;p&gt;Some of it was just blind spots. My backend habits had hidden a plain front-end fact from me: config gets frozen into the artifact at build time. It isn't read from the environment at startup the way it would be on a server. On mobile that's final; it ships embedded in the app. On web I could claw it back with a tiny Express server that serves the config at runtime. Either way I only hit it at deploy time, on the part of the project I'd filed under "the easy corner."&lt;/p&gt;

&lt;p&gt;The landing page is the clearest case. It wasn't planned at all: I just needed somewhere to host the terms of service. I built a real one anyway, because I felt like it, but kept it cheap on purpose. Astro, pushed to Cloudflare Pages, built locally, done. No Pulumi, no Dagger pipeline. A small static site wasn't worth the maintenance, time and learning the backend was.&lt;/p&gt;

&lt;p&gt;Wrapping it in the same machinery would have been absurd. (Cloudflare is exactly the managed, single-vendor thing I spent the whole infra effort pushing against. But a static site is portable &lt;em&gt;because&lt;/em&gt; it's trivial: I can move it in an afternoon. I'll dig into where "provider-agnostic" actually holds, and where it doesn't, in a separate post.)&lt;/p&gt;

&lt;p&gt;So the overkill dial wasn't set once and left there. It moved with what the project threw at me and what I had the energy for: studied on the backend, deliberately minimal on the landing page, and now and then just whatever I could be bothered with that month.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where it stands
&lt;/h2&gt;

&lt;p&gt;So where is all this now? The app is live, actually in production on a €10-a-month VPS, and it's been there a few weeks rather than sitting behind a "coming soon." A handful of friends and colleagues are the first real users. You can &lt;a href="https://play.google.com/store/apps/details?id=com.corbz.grimoire.android" rel="noopener noreferrer"&gt;get it on the Play Store&lt;/a&gt;, or have a look at the &lt;a href="https://landing.api-grimoire.ovh/" rel="noopener noreferrer"&gt;landing page&lt;/a&gt;. The app, the CI toolkit and the landing page are public on &lt;a href="https://gitlab.com/grimoire-project" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;; the infrastructure repo stays private, since it describes a cluster that's running.&lt;/p&gt;

&lt;p&gt;Building the whole thing myself, from backend to cluster to landing page, is what finally made a system click for me, after years of only operating one slice of it. The rest is too specific for one post, so it's scattered across smaller ones: the k3s crash, Pulumi's portability promise, hexagonal and DDD in practice, the search-engine swap, where I drew the line with Keycloak, and the monorepo I backed into.&lt;/p&gt;

&lt;h3&gt;
  
  
  The other posts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The three-line config I shelved, and the three-hour outage it became &lt;em&gt;(coming soon)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Building my infrastructure like I build software &lt;em&gt;(coming soon)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Everyone has an opinion about DDD. I'd never had my own. &lt;em&gt;(coming soon)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;I swapped my whole search engine in a weekend. The domain never noticed. &lt;em&gt;(coming soon)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;What I let Keycloak handle, and what I took back &lt;em&gt;(coming soon)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;The API client I published and pulled, until a monorepo just built it &lt;em&gt;(coming soon)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sideprojects</category>
      <category>kotlin</category>
      <category>kubernetes</category>
      <category>android</category>
    </item>
  </channel>
</rss>
