Very Irawan Simbolon
← Writing

My First Month at Dago Engineering: Permissions, a Bug Hiding in a Label, and Writing Before Coding

Sep 2026•6 min read

I joined Dago Engineering as a Full Stack Developer Intern on August 26, 2026. The first day was onboarding at Dago Creative Hub Singaraja, and by the next day I had opened my first pull request in a codebase I had never seen before.

Most of the month went into Stabilytics, the company's ERP for people, time, operations, and finance, where ten of my pull requests were merged. For the last ten days I moved to a new project: an internal AI assistant prototype. Between tickets I also did some product work, like benchmarking F&B point-of-sale apps, designing a wellness app prototype, and testing Griya Academy before its release.

This post is not a list of everything I did. These are the six moments that taught me the most.

A Dropdown Is Not a Lock

The task: when someone creates a task inside a project, the assignee picker should only offer people they are actually allowed to assign. A project lead can pick anyone on the project. A regular member can pick themselves and the people who report to them. Someone outside the project can only pick themselves.

Building the filter was the easy part. A breadth-first walk down the project's reporting tree produces the list of allowed people, and the dropdown simply shows those names.

The part that mattered more was on the server. Anyone can send a request without touching the form, so a name hidden from a dropdown is a suggestion, not a rule. The same check therefore runs again inside the server actions that create and update tasks, and any request that tries to assign someone outside the allowed subtree is rejected.

Earlier, review feedback on a Kanban board visibility task had taught me the other half: every access rule has exceptions. Managers with the ops.write permission still need to see everything, and that exception belongs in the rule itself, not in a patch added later.

The Org Chart That Could Loop Forever

Real teams don't always follow the company directory. On one project, B might report to A even though the directory says they are unrelated. So we added a per-project "reports to" field that falls back to the normal manager when it is left empty.

The moment people can pick their own supervisor, you can create a loop: A reports to B, B reports to C, and C reports back to A. Any code that walks up that chain would never stop.

The guard is simple: walk up the chain from the proposed supervisor, and if you meet the person being edited, the change would close a loop. A trimmed-down version:

function wouldCreateCycle(employeeId, newBossId, members) {
  if (newBossId === employeeId) return true;
  const seen = new Set();
  let current = members.get(newBossId);
  while (current && !seen.has(current.id)) {
    if (current.id === employeeId) return true;
    seen.add(current.id);
    current = members.get(current.reportsTo ?? current.managerId);
  }
  return false;
}

My first version of this guard was wrong, and it took a follow-up commit the next day to fix it. The fix was to pull the check out into a pure function: no database, no request, just data in and a boolean out. Once it stood on its own, I could write 13 small tests for self-reporting, two-person loops, and longer chains, and run them in seconds.

The Bug That Lived in a Label

My favorite bug of the month was tiny. In the "also working on it" picker, each selected person appears as a small chip you can click to remove. The problem: clicking near the field's title also removed a chip. Not the one you clicked, but always the first one.

The cause was HTML doing exactly what it is supposed to do. The field was wrapped in a native label element, and a label forwards clicks to the first interactive element inside it. Here, that happened to be the remove button on the first chip.

The fix was to replace the label wrapper with a plain container, make the whole chip the click target, and stop the click from bubbling up. A small change, but I would not have found it without going back to how labels actually behave.

Fix It Once, Not Twenty-Three Times

Stabilytics has plenty of forms with very long dropdowns, like picking a person or a project. A native select element has no search, which makes those forms tiring to use.

Instead of fixing forms one at a time, I upgraded the shared Select component. Every form that used it, across more than 23 modules, got search at once. The trick was keeping the original native select hidden on the page and always in sync, so form submissions, resets, and the existing Playwright tests kept working without a single change.

One Screen, One Question

Not all of this month's work happened in a code editor. I also designed a prototype of the Dago Wellness App in Figma, and the challenge was completely different: not "is this correct?" but "can someone read this in three seconds?"

I built the home screen around a single question: how am I doing today? The order follows how people actually open an app like this:

  • The streak at the top: a row of daily checkmarks, because the habit is the first thing people want to protect.
  • Today's activity: steps, calories burned, and active minutes as three big numbers, with no chart to interpret.
  • A supplement checklist: tick items off right from the home screen. A task this small should not be buried in another menu.
  • Long-term progress: one compact card with weight change and percentage to goal, for anyone who wants the bigger picture.
  • The next class: the only brightly colored button on the screen is "Book", because it is the only action that matters at that moment.

The lesson turned out to be close to my Stabilytics work. Deciding what not to show matters as much as deciding what to show, whether it is the names in a dropdown or the numbers on a phone screen.

Writing Before Coding

In mid-September I joined the AI Legal and AI Assistant prototype initiative with the core Dago Engineering team. It is under NDA, so I will stay at a high level. And to be clear: by the end of the month we had documents, an architecture, and a project skeleton. The part that actually finds documents and answers questions is still ahead.

The first few days were almost all writing. The product requirements document came in three parts: who the users are and what they need, how success is measured, and how the system is built. One rule from the metrics part stuck with me: no adjectives. Not "fast" or "accurate", only numbers someone else can check, plus a formula for what every thousand tokens costs.

The architecture part ended with five short decision records. My favorite is the most boring one: the system must always be able to run on a fake, offline model provider next to the real one. That way anyone can run and test the whole app without an API key.

I also learned to let go of code. One task asked for a quick Streamlit chat prototype, with that fake provider and a few smoke tests. It was never merged: after the tech lead's architecture review, the team chose a more production-ready setup, a FastAPI backend and a Next.js frontend in one repository that starts with a single command. Code that never ships can still be useful if it helps decide what should be built.

What I'm Taking Into Month Two

  • Check permissions on the server. The UI is only there for convenience.
  • When logic gets tricky, pull it into a pure function and test it there.
  • Before building a feature, look for the one shared place it should live.
  • Write the plan down with numbers. Reviews get faster and arguments get shorter.

Next month the plan is to build the retrieval part of the assistant on PostgreSQL and pgvector, with source citations and token cost tracking. I will write about how it actually goes.

Thanks to my mentors, the tech lead, the other interns, and the Dago Engineering team for the careful reviews.