I joined StrictlySurf as the sole engineer on contract in January 2026 and have been building since. The brief was simple in scope and complete in surface area: build a full-stack e-commerce and marketplace platform from zero, including a customer storefront, vendor onboarding, payments, a content layer, and any AI features that made operational sense. One engineer, one runway, no cofounder to split it with.
Four months in. The marketplace is live, the customers are real, and I am still building. This article is what I have shipped so far, the decisions that compounded, the AI feature that earned its keep, and what the constraint of one engineer is teaching me about scope and execution.
The constraint that shaped everything
The constraint was not headcount. The constraint was the cost of every decision once shipped.
When you are one engineer, every framework choice, every middleware pattern, every database schema is a thing you have to maintain alone for the next twelve months. Bad decisions do not get rolled back by a more senior teammate; they get rolled back by you, at 1 AM, on a Tuesday, when the feature you were trying to ship hits the technical debt you accumulated three months ago. So I made every architectural choice with a single lens: is this thing simple enough that one tired engineer can debug it under load.
That lens drove four decisions that compounded.
Decision 1: Postgres plus RLS instead of middleware authorization
A marketplace has authorization rules everywhere. Customers see their orders. Vendors see their products. Both see the catalog. Admins see everything. Webhooks bypass user context entirely. The naive approach is to express these rules in middleware, in route handlers, in service-layer functions. Three layers, three places to forget the rule, three places to update when the rule changes.
I put the authorization rules at the database layer instead, using PostgreSQL Row Level Security policies. Every table has policies on select, insert, update, and delete keyed to the authenticated user's role and ownership. Middleware authenticates and sets request.jwt.claims. The database does the authorization. Service-layer code calls the table directly and either returns rows or does not.
The win: the rule lives in one place. When a customer logs in and queries orders, they cannot see another customer's orders even if a route handler has a bug. When a webhook arrives without user context, it bypasses RLS via a service-role token and the schema explicitly tracks which rows came from where. When a vendor tries to update a product they do not own, the database returns zero rows updated, no exception, no leakage.
The cost: RLS policies are not the most fun thing to write. The mental model takes a week. Debugging requires you to reason about the policy stack as it executes per query. And when you introduce a new table, you have to write the policies before you ship the feature, not after.
I would make this choice again. The bus factor on a marketplace is exactly the kind of thing this protects against.
Decision 2: Idempotent Stripe webhooks at the database layer
Stripe webhooks deliver at least once. Sometimes the same payment_intent.succeeded arrives twice in three seconds. Sometimes it arrives once but your endpoint times out, retries, and you process it twice anyway. The idempotent-handling pattern is well-documented but only works if you actually do it.
I built the marketplace's webhook handlers around a single rule: every webhook event is logged with its Stripe event id as a unique constraint in a webhook_events table. The handler tries to insert the event. On conflict, the handler returns 200 immediately without doing anything. On success, the handler runs the side effect (mark order paid, credit the vendor's balance, fire the receipt email) inside the same transaction as the webhook log update.
This means duplicate webhooks become free no-ops. The system stays consistent under retries. Even if the Stripe network has a bad day, my marketplace orders do not double-charge vendors, double-credit customers, or double-fire receipt emails.
The pattern took two days to set up. It has paid for itself every week since.
Decision 3: Ledger-based store credit and refund tracking
Refunds are where naive payment systems break. A customer asks for a refund. Did they pay with a card or with store credit they earned from a return six weeks ago? Did the original payment go to one vendor or split across three? Are we refunding the marketplace fee or just the product price? The naive approach is to mutate balances and state machines until the books are wrong by a dollar and nobody notices for a quarter.
I built the credit and refund layer as a ledger. Every credit, debit, refund, marketplace fee, and adjustment is an immutable row in a ledger_entries table with a transaction id, a counterparty, an amount, and a category. Balances are derived, never stored. When a refund happens, it lands as a new ledger row. The original payment row stays untouched. The history is auditable.
This is the same pattern banks use for the same reason. A marketplace handling other people's money should match.
The cost was about a week of upfront design and a real conversation with the founder about why we were going to do it this way instead of mutating balance fields. The win is that I have not had a single reconciliation bug since the system went live.
Decision 4: Next.js App Router plus Supabase
The stack choice was less interesting than the constraint that drove it. As one engineer, I needed a framework where SSR, routing, auth, and the API layer all came from the same project. Next.js App Router gives me server components for content and client components for interaction in one tree. Supabase gives me Postgres, RLS, and an auth layer that already speaks JWT. I do not have to wire three things together; I have to learn one thing well.
I traded flexibility for cohesion. If the marketplace ever needs an event-sourced backend or a separate microservice for inventory, I will pay the migration cost. For the next twelve months, the cohesion is worth more.
The AI feature that earned its keep
The feature that justifies the AI line on my resume is the surf-report chatbot.
StrictlySurf sells surf gear. Customers want to know if today is a good day to use what they bought. The pre-AI answer was to read three different forecast sites and guess. The post-AI answer is to ask the chatbot.
The retrieval shape: NOAA buoy data plus historical condition logs plus the user's local conditions, indexed nightly into a small embedding store. The user types a question. The chatbot retrieves the top matching condition records, prepends the current NOAA snapshot, and asks an LLM to summarize: is the surf any good right now, and where should you go.
It is not magic. It is a structured retrieval pipeline with a small LLM at the end. The retrieval is the work. The model is the seasoning. When a customer asks "is Rockaway any good this morning," the answer is grounded in NOAA data the model literally just read, not in the model's training data.
What "production-adjacent" means honestly: the chatbot is shipped, used by customers, integrated into the storefront. The eval harness is light, the cost monitoring is light, the abuse protection is regex-and-prayer. None of those are showstoppers at this scale. All of them are on the roadmap. I would not call this a production AI system at FAANG scale. I would call it a production AI feature inside a real e-commerce product that had to ship to a real customer base. There is a difference, and the difference is honest.
What I would do differently
Three things.
I scoped the vendor onboarding flow too tightly in the first month. Vendors needed identity verification, banking details, tax forms, and product import in week one, and I built it as a single linear flow because it was the simplest thing to ship. Six weeks in, the founder was hand-holding new vendors through a thirty-minute call because the linear flow assumed they had everything ready. I would build a save-and-resume pattern from day one next time.
I built the search layer on Postgres full-text instead of bringing in a dedicated index. It works. It does not work great for typo tolerance and synonym matching. Rebuilding the search on a real index would have been a week sooner and saved me three iterations.
I deferred analytics for too long. By the time I added event tracking, I had two months of activity I could only reconstruct from order tables. If you are building a marketplace, instrument it on day one, even if the analytics tool is just a thin event-log table.
What this taught me about solo execution
You can ship more than people expect from one engineer. You cannot ship as much as the founder wants. The skill is in the difference.
Solo execution has a math: about thirty percent of your time goes to architecture, sixty percent to building, and ten percent to the maintenance load you accumulated last month. As the system grows, the maintenance number grows. By month four, you are not adding features at the same rate you were in month one. The system has weight, and the weight is real.
The way to keep velocity is to push the architectural decisions early so the marginal feature is cheap. RLS at the database layer means new tables are cheap to authorize. Idempotent webhooks at the database layer means new payment flows are cheap to add. Ledger-based money tracking means new fee structures are cheap to model.
The way to lose velocity is to defer architecture and push features. Three months in, you have a tangle of middleware-authorized routes and ad-hoc balance fields, and you are spending more time reasoning about the tangle than building.
I would carry this lens to a founding-engineer role at a Series A or B. The team is bigger but the lesson is the same: every architectural choice ships with a maintenance tax. Pay the tax up front, in design, where it is cheap. Or pay it later, in production debugging, where it is expensive.
The marketplace is live. The customers are real. I am still building. The bugs that have surfaced have been bugs in features, not bugs in foundations. That is what good architectural choices look like four months in.