01
Data
The source of truth and the schema that protects it. Schema, indices, constraints, the truth.
Systems · Architecture
Most early developers focus on pages. I focus on systems. A page is what someone sees. A system is what someone trusts. This page is about how I design the second one — the four layer model, the role separation, the state ownership, and the stack it all runs on.
Seven distinct surfaces, not seven copies of one dashboard.
Data, service, interface, orchestration — named on purpose.
Patterns scale. Specific tools sometimes do not.
A system is what someone trusts, not just what they see.
The mental model
I model every system in the lab in four layers. Most bugs live at the boundaries between them — validation that exists in the interface layer but not the service layer, state that exists in orchestration but is invisible to the interface. Naming the layers explicitly is the first step to keeping the boundaries clean.
01
The source of truth and the schema that protects it. Schema, indices, constraints, the truth.
02
The code that mutates the data with intent and policy. Mutation logic, policy, transactions.
03
What the user actually touches. What they see, read, edit — and nothing they shouldn't.
04
The choreography between the other three: scheduled jobs, event triggers, AI routing, observability.
Role separation
Nexli has seven roles. They are not seven copies of the same dashboard with different links hidden. Each role has its own surface, its own permissions, its own mental model of the system. A principal sees the school. A teacher sees their classes. A parent sees their child.
The lesson is general. When the system serves multiple kinds of user, separating their surfaces is the cheapest way to make the product feel correct. Trying to make one surface serve everyone is how products become bloated and confusing in equal measure.
If a single interface tries to serve every role, it serves none of them well.
Realtime, where it earns its place
Realtime is expensive. It is not just an engineering cost, it is a mental cost: the user has to make peace with the fact that the screen they are looking at might change underneath them. Realtime is the right answer when the value of seeing the change outweighs the cost of expecting it.
Attendance updating live as a teacher marks roll? Yes. Fee ledgers updating live as collection happens? Yes. A blog post showing live read counts to its author? No.
State — the part everyone underestimates
State is the silent killer of systems. Cached state that lies, optimistic state that desyncs, server state that has no client mirror, client state that has no server source. Every system the lab ships has a clear answer to one question: where does each piece of state live, and who owns it.
The rule that works for me. Server state is the truth. Client state is a projection. Cache invalidation is a system feature, not an afterthought. Write the answer to that question in a code comment near the top of the relevant file. Future you will thank present you.
Before a line of code
Three documents, kept short. The total page count is usually two. Three if the system is genuinely complex. The point is not the documents — it is forcing myself to make the structural decisions before the code starts to make them for me.
Names the entities and the relationships between them. The truth, before any code touches it.
Names who can do what. Every role, every permission, decided on paper first.
The one or two journeys that matter most, traced end to end before they are built.
Code has gravity. Sketches are weightless. Use weightlessness while you still have it.
What scales and what does not
A clean four layer model survives a stack migration; the specific framework choice may not. Designing the system at the right level of abstraction is what makes the system survive its own future.
The other thing that scales is naming. Boring, descriptive, repetitive naming. The exotic name is fun for a week and confusing for a year. The boring name compounds.
Engineering capabilities, honestly mapped
The honest answer is depth in some, working competence in others, and a deliberate plan for where the depth is being built next. Recruiters and collaborators get the truth; readers who want the trajectory get the shape of it.
01
React, Next.js, TypeScript, Tailwind, design systems, motion, 3D. A fast frontend is a values choice, not a technical one.
02
Node serverless, route handlers, server actions, REST and lightweight RPC. Honest gap: not yet shipped at tens of thousands of QPS.
03
Model orchestration, local inference, retrieval, evaluation harnesses, and the human-in-the-loop UX that decides whether a surface feels trustworthy.
04
Vercel, Firebase, Supabase, GitHub Actions. CI on every deployable surface. Less deep: Kubernetes, Terraform, full IaC.
05
Secure auth, secret management, role based mutations, server side validation everywhere already in place. Threat modeling and dependency auditing are next.
06
Mobile first responsive web is fluent. Native mobile is on the roadmap, not yet shipped under the lab's name.
The stack, on purpose
Primary language is TypeScript, secondary JavaScript when a project predates the migration. Next.js with the App Router is the default for product surfaces — it collapses routing, data fetching, image optimization and server components into a coherent default. React underneath, Tailwind for the design system, Framer Motion and GSAP for choreography, R3F when the surface needs 3D.
Firebase Firestore plus Firebase Auth for the school-system class of project, where the data is hierarchical and role separation is built in. Postgres on Supabase or Neon when the work is relational. Vercel for everything that ships to the open web — the lab does not run servers it does not have to run.
The stack is the boring part on purpose. The interesting part is what you do with it.
Beyond the stack
Stack is the named things. Technologies are the underlying ideas the named things are made of — the distinction matters because it survives stack churn. Server components by default, client components for interactivity. Static rendering, ISR, and SSR chosen per route, not per project.
Real auth is more than a login form: the role is established at sign-in and verified server side on every mutation. The client never decides what data it gets — the server does.
If the client decides what data it sees, you do not have auth. You have decoration.
Tools are the cheap part
VS Code, lightly customized. GitHub Copilot and Claude Code sit beside the thinking, not above it — AI accelerates the writing, not the thinking. Figma for layouts when a surface is genuinely complex; most work goes from sketch to component directly in the editor, because the design system already lives in the codebase.
GitHub for everything, branch per feature. Arc for daily work, Chrome for debugging, Safari and Firefox for QA. The philosophy is the same everywhere: pick the smallest set of tools that gets the work shipped.
Tool churn looks like progress and feels like motion. It is neither.
The architecture is the argument. Every pattern on this page resolves to a running system somewhere on the projects page.