DEV Community

Cover image for Dev Log: 2026-06-29
Nasrul Hazim Bin Mohamad
Nasrul Hazim Bin Mohamad

Posted on

Dev Log: 2026-06-29

TL;DR

Two threads today: an organization layer on top of an existing multi-tenant app, and driver-based password-reset backends in an identity portal. Both came down to the same idea — put the source of truth in the right place, then test it.

Multi-tenant app: an organization layer above tenancy

The product already had tenancy. What it lacked was a human-friendly layer on top: organizations users actually belong to, can switch between, and manage.

What landed:

Area Change
Org switcher A sidebar switcher to move between organizations you belong to
Management Create/update org, invitations, ownership transfer
Tenancy Resolve the active tenant from the user's org — closed a leak
UI Dark-mode pass + responsive fixes across the org views
Dashboards Richer per-widget configuration from the UI

The standout is the tenancy fix: the active tenant was being resolved from the request instead of the authenticated user. I pulled that into its own focused post — "Resolve the tenant from the user, not the request." Short version: if a value scopes data, it can't come from something the client controls.

Identity portal: make the reset backends swappable

The password-reset flow needed to support more than one backend, and let an admin decide the order they run in. Classic case for a driver-based abstraction — a contract plus interchangeable drivers, picked at runtime from config.

interface PasswordResetBackend
{
    public function reset(User $user, string $password): void;
    public function name(): string;
}
Enter fullscreen mode Exit fullscreen mode

Two optional backends came back as drivers behind that contract, and the run order is now admin-reorderable instead of hard-coded. Adding a third backend later is a new class + a config line — no touching the flow itself.

The other half of the day was unglamorous but necessary: the test suite had drifted — stale tests for removed features, and env leakage between tests (one test's state bleeding into the next). Fixed the leakage, deleted the dead tests, and the suite is honest again.

Takeaway

Two different apps, one lesson repeated: decide where the truth lives (the user's org; the config-driven driver), make the boundary explicit, and pin it with a test. Everything else is UI.

Top comments (0)