<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" 
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Hendra Codes - Blog</title>
    <link>https://hendracodes.com/blog</link>
    <description>Hire a professional mobile and web developer with 5+ years experience. Specializing in Flutter, React, and Next.js. Available for freelance projects and full-time opportunities.</description>
    <language>en-US</language>
    <lastBuildDate>Wed, 15 Apr 2026 09:00:00 GMT</lastBuildDate>
    <atom:link href="https://hendracodes.com/rss.xml" rel="self" type="application/rss+xml"/>
    <copyright>© 2026 Hendra. All rights reserved.</copyright>
    <managingEditor> (Hendra)</managingEditor>
    <webMaster> (Hendra)</webMaster>
    <generator>Next.js</generator>
    <image>
      <url>https://hendracodes.com/og-image.svg</url>
      <title>Hendra Codes</title>
      <link>https://hendracodes.com</link>
    </image>
    <item>
      <title>How I Built MultiTik: Designing a Timer App That Feels Simple</title>
      <link>https://hendracodes.com/blog/how-i-built-multitik</link>
      <guid isPermaLink="true">https://hendracodes.com/blog/how-i-built-multitik</guid>
      <description><![CDATA[Every phone already has a timer, but it runs one at a time and too many of them are unreliable. MultiTik runs many, and makes sure they always go off. Here is how I kept it simple while doing more.]]></description>
      <content:encoded><![CDATA[Every phone already comes with a timer. So why build another one?

Because the built-in timer almost always does one thing: one timer at a time. Real life is rarely one timer at a time. You are boiling eggs while the rice cooks. You are running a workout with work and rest intervals. You are studying in focused blocks with something else going in the background. The moment you need a second timer, the default app starts fighting you.

That is the whole reason MultiTik exists. It runs multiple timers at once, each one independent, with its own label, sound, and alarm. You can pause one, add five minutes to another, and reset a third without touching the rest.

### The dirty secret of timer apps is that they are not reliable

When I looked at the timer apps people actually use, I noticed something. A lot of the loudest reviews are not about features. They are complaints. Timers that die the moment the screen locks. Notifications that never fire. Apps that crash in the middle of a workout or a recipe. Even popular, highly rated apps have this problem.

That changed how I thought about the whole project. A timer has exactly one job: go off when it is supposed to. If it fails at that, every nice feature on top of it is worthless. So the real promise of MultiTik is not "more timers." It is timers that actually fire, even when the app is in the background and the phone is locked. Getting that right, every time, is the part I spent the most care on.

### Doing more without it feeling like more

The trap with a multi-timer app is obvious: more timers means more clutter. More numbers, more buttons, more ways to tap the wrong thing. If checking your timers feels like work, people go back to the default app.

So most of the design was about restraint. Timers have to be readable at a glance and reachable with a thumb while your hands are busy with the actual task. The buttons stay in the same place no matter what state a timer is in, so you never go hunting for them. Small touches do a lot of the work here: a "done at" time on each card, quick plus-one and plus-five minute buttons, and a visual countdown ring that lets you read progress without reading numbers.

I added depth too, but only where it earns its place. You can chain timers to run in sequence, set up interval routines like Tabata or HIIT, and group timers by activity. And I deliberately said no to a long list of things, at least for now: voice control, cross-device sync, fancy cooking-coordination modes. Shipping a focused, reliable core mattered more than a long feature list.

### The things I refused to compromise on

Two decisions were never really up for debate.

No ads, and no subscription. People genuinely resent paying a monthly fee for a timer, and "price gouging for a timer" is a real category of one-star review. A small utility like this should not nickel-and-dime you to use it.

And accessibility is built in, not bolted on as a separate mode. There is a high-contrast theme, and timer status is shown with icons and shapes, not color alone, so it works for colorblind users. The part I like is that none of this helps only a subset of people. Bigger touch targets prevent everyone's mis-taps. A clear visual countdown helps anyone who struggles to feel time passing. Designing for the edges usually makes the middle better too.

### Why Android first

I built for Android first, and it was a deliberate call.

The hardest part of a timer app is exactly the part iOS is strictest about: running reliably in the background and firing notifications on time. Android gave me more room to get that core experience right with fewer fights against the platform, so I could prove out the reliability before taking on iOS's stricter rules.

### The point

There is nothing flashy about a timer app. But a lot of people quietly want exactly this: more than one timer, that always goes off, without ads and without clutter. It is a small, common, real need that the default tools do not serve well.

That is the kind of thing I like building. Not reinventing anything dramatic, just taking something everyone already uses, removing the one annoying limit, and making sure the basics never let you down. The win was never the feature list. It was making five timers feel as simple, and as trustworthy, as one.]]></content:encoded>
      <pubDate>Wed, 15 Apr 2026 09:00:00 GMT</pubDate>
      <dc:creator>Hendra</dc:creator>
    </item>
    <item>
      <title>Building Aksademia&apos;s Online Test Engine Was Harder Than I Expected</title>
      <link>https://hendracodes.com/blog/building-aksademia-online-test-engine</link>
      <guid isPermaLink="true">https://hendracodes.com/blog/building-aksademia-online-test-engine</guid>
      <description><![CDATA[EdTech is crowded, but building a live exam platform that actually feels reliable is still hard. This is what I learned while building Aksademia's test engine.]]></description>
      <content:encoded><![CDATA[EdTech is crowded. Courses, question banks, tryout platforms, learning apps, AI tutors. The market clearly exists, and plenty of people already pay for it. But looking at the space as a developer in Indonesia, I kept thinking the same thing: why is it still hard to find one centralized place that gives a really good live test experience?

That was one reason I started building Aksademia. I did not want just another page with questions and a submit button. I wanted it to feel like a real exam: timed, focused, harder to cheat, and built to hold up when a lot of people use it at once.

Which sounds simple until you try to build it.

### A test engine is not just a quiz

On the surface it looks easy: show question, pick answer, next, submit, score.

The moment you think about real exams, it gets serious. The timer is not a cute countdown. It decides when the session ends. Answers cannot vanish because someone refreshed. Navigation has to feel fast because students are already under pressure. Add anti-cheat, fullscreen, multiple question types, and result review, and it stops being a quiz. It becomes a system.

### The same three bugs kept coming back

The test engine was the hardest part so far, and the strange thing is the bugs were almost always the same three problems wearing different costumes: keeping exam state server-backed, getting the timer right, and persisting answers reliably.

I fixed those more times than I want to admit. You solve the timer, then a student refreshes mid-exam and the browser and server disagree about how much time is left. You get persistence working, then a connection drops for ten seconds and you have to decide what the truth is when it comes back.

So the decisions that actually mattered were boring on paper:

- The timer is server-authoritative. The countdown on screen just reflects it. The server decides when time is up, because the browser can be refreshed, paused, or simply lied to.
- Answers autosave as the student goes, not only on submit.
- If a student refreshes or reconnects, they resume where they left off: same question, same remaining time, same answers.

None of that is glamorous. But it is the whole difference between a demo and something a student can trust during a real exam. The technical rule, make the server the source of truth, only exists because of a product promise: a student should never lose their progress or their time.

### Anti-cheat is also a UX problem

Aksademia does the expected things: it requires fullscreen, detects tab switching and window blur, blocks copy and right-click, and counts violations. Getting those to fire was not the hard part.

The hard part is the balance. Too aggressive, and students feel punished for a flaky connection or an accidental click. Too soft, and the exam stops feeling like an exam. Every check is a dial, and turning it too far either way makes the product worse. The goal is not really "catch cheaters". It is an environment that feels focused and fair, the same way the real exam has boundaries.

### Live, but the real test is still coming

I want to be straight, because exam platforms love to talk about scale. Aksademia is live. Anyone can sign up and take a real exam on it. What I have not done yet is throw a big push behind it, so I have not watched a huge crowd hit it all at once. I have stress-tested for that load, but stress testing and exam night are not the same animal.

The real proof is a room full of students all starting at 7pm. That is still ahead of me, and I would rather say so than pretend the hardest part is behind me. What I do know is that the boring foundation (server-authoritative timing, autosave, resume) is exactly what you want solid before that crowd arrives, not after.

### What I took from it

Online exams are harder than they look. A quiz app you can build quickly; a real exam platform needs care around trust, timing, cheating, and the moments when things go wrong. None of this was built with unlimited time or a big team, which is exactly why I had to be ruthless about what to make solid first: the exam flow, before anything cosmetic. A nice landing page does not save a broken timer.

The product decisions mattered as much as the technical ones. A timer can work and still cause anxiety if the UI is unclear. That is why I like building things end-to-end: you cannot hide behind one layer. Even with all the iterations on those same three stubborn problems, taking a crowded space and asking "can I make this better?" is the part I enjoy. And honestly, that is what makes it worth building.]]></content:encoded>
      <pubDate>Sun, 15 Mar 2026 09:00:00 GMT</pubDate>
      <dc:creator>Hendra</dc:creator>
    </item>
    <item>
      <title>Deploying My Portfolio with Dokploy, Docker, and Next.js</title>
      <link>https://hendracodes.com/blog/deploying-portfolio-dokploy-docker-nextjs</link>
      <guid isPermaLink="true">https://hendracodes.com/blog/deploying-portfolio-dokploy-docker-nextjs</guid>
      <description><![CDATA[I moved my portfolio off a managed host and onto my own VPS with Dokploy. Here's the actual setup: Docker Compose, Postgres, MinIO, and the things I check every time I deploy.]]></description>
      <content:encoded><![CDATA[Building the app is the part everyone shows you. Deploying it is the part that quietly decides whether the project is real.

For a long time my mental model of "deploy" was: push to a managed platform, watch a progress bar, get a URL. That works, and for a lot of projects it is the right call. But the honest reasons I moved my portfolio onto my own VPS are cost and control. Vercel and Railway are genuinely good, but the moment you have more than a toy project they get expensive next to a private server. I am mostly a frontend developer, so I first went down this road to learn the DevOps side, did not expect to like it, and now I am a little addicted to the control.

So I moved it onto a VPS and let Dokploy run it. Here is what that looks like.

### It is not one thing, it is a few that have to agree

On my laptop, the portfolio is one `npm run dev` away. In production it is several services that have to behave like one product: the Next.js app, a Postgres database for every project and blog post, MinIO serving the uploaded screenshots and videos, and a small migrate job that runs the database migrations before the app starts.

The whole stack lives in a single `docker-compose.yml`, which matters more than it sounds. Instead of clicking around a dashboard to wire an app to a database to a bucket, the relationships sit in one file in the repo, and services talk by container name over a shared network, so nothing breaks when the host reboots and addresses shuffle.

The migrate job is one I would have skipped when I was younger and more confident. Dokploy runs `prisma migrate deploy` on every deploy, so the schema is never behind the code that expects it. You forget that step exactly once.

### The data is the part you can't redo

Here is the thing nobody tells you when you self-host: the app is disposable, the data is not. I can rebuild the container endlessly, but I cannot rebuild a database I accidentally wiped, or re-upload screenshots that only lived inside a volume I deleted.

So the two most important lines in the whole setup are named volumes: `postgres_data` for the database, and `minio_data` for every image and video I have uploaded. They persist on the host through any redeploy or restart. Everything else is disposable; these two I treat as precious. The media is served through a CDN domain (`cdn.hendracodes.com`) pointed at MinIO, so the images on the live site come straight out of that volume.

### What Dokploy actually does for me

I could run `docker compose up -d` over SSH and call it a day, and that fallback always works. What Dokploy adds is the boring glue I did not want to hand-maintain: it rebuilds from the repo on deploy, runs Traefik in front so routing and Let's Encrypt SSL just happen for both domains, gives me one place to set environment variables without committing them, and keeps the volumes wired into its backup system so the database and media can be scheduled off-box.

The mental shift is that I stopped treating deploy as an event I babysit and started treating it as a button. The Compose file is the source of truth; Dokploy presses play.

### What I check after every deploy

A green deploy is not a working site. I check the same short list every time, because each item has burned me at least once:

1. **The site loads.** The actual homepage, not just a 200 from a health check.
2. **An image renders.** That proves the app, MinIO, the CDN domain, and the bucket policy are all on speaking terms.
3. **The sitemap and RSS feed** at `/sitemap.xml` and `/rss.xml`. If either is empty or stale, search engines and subscribers see a broken site even when humans do not.
4. **Metadata.** Title, description, and OG tags, because a broken share preview is the kind of thing you notice weeks too late.

None of these are clever. That is the point. The clever bugs are rare; the boring ones are constant.

### Was it worth it?

I will be honest about the tradeoff, because self-hosting is not for everyone. Running your own server is simply harder. You now own a long list of fiddly things a managed platform handled for you: SSL certificates, DNS records, IPs, ports. None are hard alone, but they all have to be right at once, and when a site will not load, it is usually one of them quietly misconfigured.

So my advice is boring. If you are starting out, or you only have one simple project, a managed host is the better choice. Let someone else handle the plumbing while you build. It is when things get complicated and you actually want control that a VPS earns its place.

For me, self-hosting taught me more about how my own product runs than any managed deploy ever did. When everything is abstracted away, you never have to understand it. When the database, the storage, and the proxy all sit in one Compose file you wrote, you cannot hide from any of it. For a portfolio, a site whose whole job is to say "I can build and run real things," that felt like the honest way to deploy it.]]></content:encoded>
      <pubDate>Sun, 15 Feb 2026 09:00:00 GMT</pubDate>
      <dc:creator>Hendra</dc:creator>
    </item>
    <item>
      <title>Building Production Flutter Apps: My Practical Checklist</title>
      <link>https://hendracodes.com/blog/production-flutter-app-checklist</link>
      <guid isPermaLink="true">https://hendracodes.com/blog/production-flutter-app-checklist</guid>
      <description><![CDATA[Most of my Flutter production checklist is boring foundational work I do before the app actually needs it. Here is what I check before calling an app production-ready, using MultiTik as the example.]]></description>
      <content:encoded><![CDATA[MultiTik is a timer app. On the surface that is about as simple as a mobile app gets: start a timer, watch it count down, done. But there is a real gap between "it runs on my phone" and "it is ready for the Play Store," and most of my checklist lives inside that gap.

The honest theme of everything below is this: structure first, even when it feels like overkill. A lot of these steps look like too much for a small app, which is exactly why people skip them. But they are the things that decide whether the app stays easy to work on once it grows, so I do them early on purpose.

### Decide your structure before you write features

Before I build any real feature, I set up the structure and try to follow SOLID from the start. It feels slow. You are writing folders and abstractions for an app that does almost nothing yet. But the alternative is worse: a small app that turns into a tangle the moment you add a second screen.

Part of that is picking your state management deliberately. In Flutter this is one of the first real decisions you make, and changing it later is painful because it touches everything. Plain setState is fine for something tiny, but I choose based on where the app is going, not where it is on day one. The same goes for navigation and local persistence. Decide the approach early, because both become load-bearing once features start piling on.

I also try to stay on current packages and follow what the Flutter community treats as best practice. The ecosystem moves fast, and fighting it usually costs more than following it.

### Set up flavors and environments on day one

If there is one thing I now set up earlier than I used to, it is flavors and environments.

Dev, staging, and production should be separate from the start: different API endpoints, different app icons, sometimes different bundle IDs so they can sit on the same phone. Wiring this in later, after the app already has real users and real data, is a headache. Doing it on day one costs almost nothing and saves you from the classic mistake of pointing a debug build at production.

### Testing is not optional

For me, testing is a must, even on a "simple" app. A timer is a perfect example. The logic looks trivial until you actually test the edge cases, and that is exactly where the embarrassing bugs hide. I would rather a test catch it than a one-star review.

### Build the safety net before you need it

Two tools earn their place in every app I ship.

Crashlytics is the first. It is the difference between learning about a crash from data and learning about it from an angry review weeks later. The moment real users are involved, you want to see what is actually breaking on devices you do not own.

CI/CD is the second. Builds and releases should be repeatable, not a manual ritual you half-remember each time. Even a simple pipeline removes a whole category of "it worked on my machine" release-day stress.

### Do not ship the default Flutter look

This one is more taste than tooling, but it matters. Flutter's defaults look very generic: the stock Material blue, the default fonts, the default splash. Users cannot always name it, but they can feel when an app looks like a fresh `flutter create`.

So part of "production ready" for me is identity. A real app icon, a proper splash, a considered theme, and a name that fits. While you are there, check the permissions you actually request, because asking for more than you need is both a trust problem and a common reason for store rejections. And treat the store listing as part of the product. Decent screenshots and a clear description are not something to rush the night before launch.

### The point of all the overkill

Most of this looks like too much for a timer app, and for a weekend throwaway it probably is. But the reason I keep this checklist is that the "overkill" is really just an investment. Structure, flavors, tests, crash reporting, and a real identity are cheap to add early and expensive to retrofit later.

It is the difference between an app you can keep building on and one that fights you the moment it grows. Future me always thanks past me for doing the boring parts first.]]></content:encoded>
      <pubDate>Thu, 15 Jan 2026 09:00:00 GMT</pubDate>
      <dc:creator>Hendra</dc:creator>
    </item>
    <item>
      <title>How I Structure Full-stack Side Projects Before They Get Messy</title>
      <link>https://hendracodes.com/blog/structuring-full-stack-side-projects</link>
      <guid isPermaLink="true">https://hendracodes.com/blog/structuring-full-stack-side-projects</guid>
      <description><![CDATA[Most side projects get messy because they start with "let's see how it goes." I do the opposite: I plan, spec, and document before I write a line of code. Here is why, and what it actually looks like.]]></description>
      <content:encoded><![CDATA[There is a very common way to start a side project: open the editor, start coding, and figure out the structure as you go. "Let's see how it goes."

I do almost the opposite. Before I write a single line of real code, I want the full picture. What the product is, what surfaces it needs, where the data lives, how the pieces talk to each other. I map it, I spec it, and I write it down. Only then do I start building.

That probably sounds slow, and at the very start it is. But I have learned that the slow part is cheaper than the alternative.

### I plan before I code, on purpose

For a project like Berse, the first work is not code. It is mapping. I write specs, I sketch the surfaces, and I get the structure clear before any features exist.

The docs I write at this stage are not busywork. They are blueprints. When I come back to a project after two weeks on something else, the plan is what lets me pick up where I left off instead of re-deriving my own decisions. Future me reads them more than anyone.

This is also why I lean toward over-planning. I know "over-engineering" is usually said as an insult, and for gold-plating features it should be. But I would much rather spend the extra time up front getting the structure right than ship something simple, hit bugs, and tear it apart in a refactor later. Redoing work is the most expensive thing I can do, so I try to design it out before it happens.

### Pick the architecture for where it is going

I do not reach for microservices by default. A lot of the time a monolith with good modularity is the nicer answer. It is simpler to run, simpler to reason about, and perfectly capable when the boundaries inside it are clean.

Microservices earn their place when things genuinely get complicated, and I can go polyglot when a specific service deserves a different tool. The trick is not picking the fanciest architecture. It is picking the one that matches the real complexity you have now, while keeping the seams clean enough that growing later is an option instead of a full rewrite.

### The project that taught me all of this

I did not arrive at "plan first" from a book. I arrived at it by rebuilding the same platform more times than I would like to admit.

That platform is Youth Break the Boundaries, or YBB, which powers a family of youth summit programs and their sites, like istanbulyouthsummit.com and chinayouthsummit.com. I have been developing it for years, and it has been on a long journey: from CodeIgniter 3 to CodeIgniter 4, and eventually over to JavaScript and TypeScript. It started as a simple monolith and grew toward a full microservice super app serving many summits at once.

Every one of those jumps taught me the same lesson. The parts that hurt to migrate were the parts I had not planned for. The parts that moved cleanly were the ones I had structured with some foresight. After enough of that, "let's see how it goes" stops sounding relaxed and starts sounding expensive.

### Keep it boring where it counts

The other thing years of running things in production taught me is to be boring on purpose where it matters most.

When an app is live, uptime is a feature. I care a lot about keeping downtime near zero during deploys, and about treating performance and optimization as things I design for, not things I bolt on after users complain. None of that is exciting, but a side project that quietly stays up and stays fast earns more trust than a clever one that flakes.

### The actual takeaway

If there is one thing I would want someone to take from this, it is simple: plan first. Get the picture clear before you start, not after.

I am not against moving fast. I am against moving fast in a direction I have not thought about, because that is exactly how a side project gets messy. Clarity at the start is what keeps the middle from falling apart.]]></content:encoded>
      <pubDate>Mon, 15 Dec 2025 09:00:00 GMT</pubDate>
      <dc:creator>Hendra</dc:creator>
    </item>
    <item>
      <title>AI Will Not Replace Developers, But Developers Should Learn to Use It</title>
      <link>https://hendracodes.com/blog/ai-will-not-replace-developers</link>
      <guid isPermaLink="true">https://hendracodes.com/blog/ai-will-not-replace-developers</guid>
      <description><![CDATA[Anyone can build with AI now, even non-developers. But the hard parts of software did not disappear, they just moved to whoever knows how to steer the tool. Here is how I actually use AI, and why it will not replace developers who learn to use it.]]></description>
      <content:encoded><![CDATA[Every few weeks someone announces that AI is about to replace developers. I do not buy it. But I also do not think you get to ignore AI and be fine. Both of those are true at the same time, and that is the part people keep missing.

Here is what is actually happening. Anyone can build something now. You do not even need to be technical. People are spinning up landing pages and simple apps with AI that would have taken them weeks to learn before. That is genuinely impressive, and it is not going away.

But building something and building something that survives contact with the real world are two very different things.

### The easy part got easy. The hard part did not

The stuff AI makes trivial was always the easy part. A landing page. A basic CRUD app. A form that submits.

What it does not hand you for free is everything that actually decides whether a product holds up: security, performance, scalability, the boring reliability work. A lot of people building with AI right now have not touched any of that yet, often without realizing it. The app works in the demo, so it must be done. Then real users show up, and the gaps that were invisible become the whole problem.

That gap is exactly where developers still matter.

### You cannot steer what you do not understand

AI knows almost everything. That part is real. But it does not drive itself. It needs to be steered, and steering it well is its own skill.

This is what I would tell anyone excited about vibe coding. To get good results out of AI, you still need to know the terms. You need to understand what you are asking for, recognize when the answer is wrong, and know what to ask next. If you do not know the vocabulary, you cannot tell the difference between a good answer and a confident, wrong one, and the AI will give you both in exactly the same tone.

So the knowledge did not stop mattering. It moved. It used to be "can you write this code." Now a lot of it is "do you know enough to steer this and catch its mistakes."

### How I actually use it

I use AI for a lot, and I am not shy about it. I ask it about technologies, I have it help me draft, I lean on it for debugging, and most of all I let it write the cumbersome code: boilerplate, refactoring, the repetitive stuff that does not need to be done by hand.

It is great at all of that, with supervision. The supervision is not optional. AI hallucinates the moment you stop giving it proper context, and there are still plenty of times where I find it faster to just write the code myself than to explain exactly what I want. Knowing which situation you are in is part of the job now.

### A baby with the brain of a genius

The way I describe AI is that it is like a baby with the brain of a genius. It knows an unbelievable amount, but it still needs guidance to do the right thing. Point it well and it delivers something great. Leave it to its own assumptions and it confidently wanders off.

The clearest example for me is code. I let it handle the boilerplate and the repetitive work, then I make the calls on structure, on what is actually correct, and on what to throw out. The AI does the heavy lifting. I keep the judgment. That split is exactly how I think it should work.

### What I would tell other developers

Learn to use it. Seriously. AI is useful, it keeps getting better, and pretending otherwise just puts you behind.

But use it the way you would work with a brilliant, fast, slightly reckless assistant. Let it boost your productivity, and keep your hand on the wheel. It will not replace developers. The developers who learn to steer it well will simply get a lot more done than the ones who refuse to.]]></content:encoded>
      <pubDate>Sat, 15 Nov 2025 09:00:00 GMT</pubDate>
      <dc:creator>Hendra</dc:creator>
    </item>
    <item>
      <title>How I Ended Up Building Things for a Living</title>
      <link>https://hendracodes.com/blog/how-i-ended-up-building-things-for-a-living</link>
      <guid isPermaLink="true">https://hendracodes.com/blog/how-i-ended-up-building-things-for-a-living</guid>
      <description><![CDATA[From borrowing laptops in internet cafés to building full-stack systems and riding through Jakarta nights, this is the story of how I went from a kid who loved Pokémon to a developer who loves making things work.]]></description>
      <content:encoded><![CDATA[# How I Ended Up Building Things for a Living  
*(or: that kid who just wanted to play Pokémon on his phone)*  

I grew up glued to mobile games. My old Java phone was basically a tiny game console with Pokémon as the main character of my childhood. That’s also how I learned English, one menu option and story dialogue at a time. Somewhere between catching Pikachu and running out of phone memory, I thought, “One day, I want to make my own game.”  

So I did what any starry-eyed teenager would do. I chased it. I signed up for Informatics Engineering in college, ready to build the next big thing.  

### The not-so-glamorous start  
Turns out chasing dreams is easier when you actually own a computer. I didn’t. I spent my first semester borrowing laptops from friends and hopping between internet cafés to finish assignments. My grades were terrible. Everyone else had studied programming in vocational schools and knew what they were doing. I could barely figure out how to run code without breaking something.  

But that feeling of being left behind did something to me. It lit a fire. I started studying longer, breaking and fixing the same code until it finally worked. Slowly, things began to click. I went from the guy who barely understood loops to joining national coding competitions, actually winning a few, and later graduating summa cum laude. Not bad for the kid who started out Googling “how to print hello world.”  

### My first real project  
In one of my early web classes, we had to build a simple app using plain PHP. I decided to go off-script and use CodeIgniter 3 instead. My lecturer told me to keep it simple, and I nodded politely before doing the opposite. A week later, I turned in a full sales system with authentication, CRUD, and everything working perfectly. It ran well, and I was proud. That was the first time I thought, “Okay, I can actually do this.”  

### Falling in love with Flutter  
In college, I discovered Flutter. The idea of writing one codebase and shipping it to multiple platforms felt like magic. I jumped in fast, built a bunch of projects, and never really looked back.  

Before that, I was into game development. But making games needs a small army of designers, artists, and sound engineers. Flutter gave me the part I loved most, the logic and the interaction. I could turn ideas into something people could actually use, and it scratched the same creative itch without the chaos.  

### Curiosity got out of hand  
I’ve never been good at sticking to one thing. Once I understood mobile, I wanted to understand everything else. It wasn’t enough to build the app; I wanted to know what happened under the hood. I dove into backend work, APIs, servers, databases, infrastructure, anything I could get my hands on.  

I like details. I like control. I love experimenting just to see how something works or if there’s a better way to do it. That curiosity slowly turned me into a full-stack developer, even though I never planned to be one.  

### The ritual of focus  
These days, music is my coding partner. I usually wear one AirPod at a time so the battery lasts longer. When I’m in serious mode, both go in and the world disappears. I’ve done full-time work, remote gigs, and freelance projects, sometimes all at once. Coding feels like solving puzzles all day, except the puzzles pay rent.  

### Life outside the screen  
When I’m not coding, I’m usually out moving. I love going to the gym, running, or playing badminton. Riding my sport bike at night is one of my favorite things to do, especially when the streets of Jakarta finally calm down. It’s the one time the city feels peaceful, and it clears my head better than any break ever could.  

I’m a big fan of anime and movies, especially ones with worlds full of magic or mystery. Attack on Titan, Demon Slayer, stories that make you forget where you are for a while. I love learning new languages too, both programming and human. I speak Indonesian, Sundanese, Javanese, and English, and I’ve been learning Spanish, Turkish, and now Japanese, partly for fun and partly to enjoy anime without subtitles.  

### Why I built this portfolio  
This site is a reflection of all that. It’s not just a collection of projects; it’s the story of how I grew into the developer I am now. From the kid borrowing laptops in cafés to someone who can build across the stack, it all connects.  

I still get that same feeling I had as a kid, wondering how things work and wanting to create something cool. I don’t know where this road leads, but I’ll keep coding, learning, breaking, fixing, and building along the way. Maybe with a cup of coffee in one hand and one AirPod still hanging on one percent battery.  

Because honestly, that’s the fun part.]]></content:encoded>
      <pubDate>Sun, 12 Oct 2025 03:11:59 GMT</pubDate>
      <dc:creator>Hendra</dc:creator>
    </item>
  </channel>
</rss>