Build it. Break it. Understand it. Improve it.
I learn engineering by working through real systems: understanding the problem, designing the flow, building the product, tracing failures and getting it into a state where people can actually use it.
Think in systems, not isolated files.
A feature is rarely just a component or endpoint. It is a chain of decisions connecting a user, application state, APIs, data and infrastructure.
What the user sees and triggers
Communication and live updates
Business rules and state transitions
Persistent application state
Specialized product capabilities
The environment where it runs
Tools are useful when they solve a real constraint.
My stack is broad because the products I build cross boundaries—not because collecting technologies is the goal.
APIs & business logic
Java, Spring Boot, REST APIs, JPA/Hibernate and service-layer design for application workflows.
Product from UI to DB
React and Next.js connected to backend services, authentication, persistence and responsive interfaces.
AI as a product feature
OpenAI and Retell AI integrations where models and voice are part of a larger application workflow.
Persistent application state
PostgreSQL, MySQL, Supabase and relational modeling with application behavior in mind.
Auth & protected flows
JWT-based API access and Clerk authentication/organization workflows across full-stack products.
Live system behavior
Server-Sent Events and state transitions for experiences where users need current system status.
From uncertainty to a working system.
Understand
Define the user problem and the system behavior before reaching for a framework.
Design
Break the problem into boundaries, data, APIs and explicit states.
Build
Implement the smallest useful path while keeping the architecture understandable.
Observe
Use logs, browser tools, requests, responses and database state to see what is really happening.
Debug
Trace the failure back to its root cause instead of patching the visible symptom.
Ship
Deploy, verify the production path and iterate from what the system teaches you.
Follow the evidence.
When something breaks, I start from the symptom and work backwards through the system instead of guessing.
The browser rejected API requests because the frontend origin did not match the backend configuration. The useful move was to inspect the actual request and response headers, then fix the Spring configuration.
Some requests contained a JWT while others did not. Checking client storage, request behavior and protected endpoints helped separate an auth-state problem from a backend failure.
A 400 response is information. Rather than hiding it in the UI, trace the endpoint contract, inputs and server-side validation to understand why the request was rejected.
An SSE connection returning 404 required checking the browser URL, controller mapping and server routing together—not assuming the frontend was at fault.
The goal isn't to avoid every bug. It is to become faster at turning an unknown failure into a known cause and a deliberate fix.
How I make engineering decisions.
Clarity
Prefer understandable architecture over unnecessary complexity. A system should be explainable before it is impressive.
Ownership
Take a feature from problem definition through implementation, debugging, deployment and iteration.
Evidence
Logs, network requests, response headers and database state beat assumptions when a system fails.
Boundaries
Keep responsibilities clear between UI, APIs, business logic, persistence, identity and external services.
Iteration
A working first version is a starting point. Feedback and failures reveal what the next version needs.
Users
Technical decisions matter because they make a product more useful, reliable or easier to operate.
See the engineering in context.
Doctor Queue shows the backend and real-time side. FoloUp AI shows the AI, voice and multi-service integration side.