Back to Blog

Foundation First

I built Uforiya solo. It's a B2C (Business to consumer) music collaboration platform where artists, producers, and engineers work together on unreleased music in one place.

Before writing a single line of code, I made three critical architectural decisions:

These decisions sound slow. Conventional wisdom says: build the UI first, show results, iterate fast.

I did the opposite. And I shipped faster, more securely, and more confidently than if I'd followed the typical path.

Here's why I made each decision, what they cost, and what they gained.

Understanding the Problem First

Before architecture, there was research.

I spent weeks studying the music collaboration space. I also make beats in my free time, so I had firsthand knowledge of these workflows. I looked at existing tools like Splice, Pibox, and MusicTeam to understand what worked and what didn't. I had conversations with real producers about their daily processes: how they manage versions, how they invite collaborators, what frustrates them about existing solutions.

I created detailed requirements based on what I learned. Only then did I choose the tools.

Why this mattered: Requirements drove architecture. I didn't pick technologies because they're trendy. I picked them because they solved specific problems I'd identified in real workflows. This wasn't theoretical. It came from experience and direct feedback.

This upfront work felt slow. It saved weeks of rework later.

Decision 1: Serverless Infrastructure

The Context

I was solo. No DevOps person. No infrastructure team. Just me.

If I had to manage servers, patch security updates, monitor uptime, and make scaling decisions, I wouldn't only be building features. I'd also be doing DevOps.

The Choice

I chose serverless infrastructure (Vercel, Supabase, cloud storage):

What This Meant

No servers to manage. No scaling decisions. No 2 AM wake-up calls.

When traffic spikes, Vercel scales automatically. When I deploy (make updates to the app), it goes live instantly. When a security patch drops, the platform handles it.

I focus entirely on code. The platforms handle operations.

The Cost

Serverless comes with tradeoffs. You're dependent on managed platforms rather than self-hosting. You have less direct control over infrastructure details.

But here's the thing: as a solo developer, managing infrastructure isn't a competitive advantage. It's overhead. The time I'd spend patching servers, monitoring uptime, and scaling manually is time I'm not using to build my product.

For a solo founder, that tradeoff is worth it every time.

Decision 2: Client-Server Architecture

The Problem

Most indie hackers skip backend architecture. They build the UI first, assuming they'll "figure out the backend later."

This works for simple apps. For something like Uforiya, it's a disaster.

Why? Uforiya has complex permission rules:

If you put this logic in the frontend, one thing happens: it breaks.

The Solution: Client-Server Architecture

Client-server means:

User Action (Frontend)

Backend Validates (permissions, input, business rules)

Backend Modifies Database

Backend Returns Result

Frontend Displays Result

Why This Matters for Security

The frontend is the most exposed layer. Users interact with it directly. If you put permission logic there, users (or hackers) can bypass it.

Opening developer tools, a user could:

With client-server architecture, none of this works. Every request goes through the backend. The backend re-validates everything.

Your permission check doesn't live in the UI. It lives in the API.

The Benefit

Permission bugs become nearly impossible. The frontend can't accidentally expose private data. Hackers can't manipulate the client to gain access.

Security isn't a feature bolted on later. It's baked into the architecture.

Why This Matters for Multiple Platforms

In the next phase of Uforiya, I plan to build a mobile app (React Native).

With client-server architecture, I don't need to rebuild the backend. I will be building a new frontend (mobile app) that calls the same API.

With frontend-heavy architecture, I'd have to rebuild all the permission logic, all the validation, and all the business rules in React Native.

Client-server architecture means: different UIs, same backend, identical security.

Decision 3: Backend-First Development

The Analogy

When you build a house, you don't start with the walls. You start with the foundation.

If you build the walls before the foundation is solid, two things happen:

  1. When the foundation settles, cracks appear in your walls and you have to rebuild them
  2. If the foundation is unstable, the whole house fails

Software is the same. If you build the UI before the backend is solid, you either:

  1. Spend weeks rewriting the UI to match the backend
  2. End up with a fragile system that breaks in production

My Approach

I built Uforiya bottom-up in this order:

  1. Database schema: designed how projects, tracks, and versions relate. Decided what's immutable (created_by never changes) and what's mutable (owner_id can transfer). Embedded business rules directly into the schema.
  2. Backend API: built authentication, project management, and permission-checked operations. Tested every endpoint before the frontend existed.
  3. Audio worker: processes uploaded audio files to extract information (duration, waveform, BPM, key), convert to streaming format, and store results. Triggered by the backend when users upload, with job status tracking and automatic retries.
  4. Frontend: built the UI to call proven backend APIs. No permission logic needed. Just display what the backend allows.

Why This Order Mattered

With this approach, the hard problems (permissions, data relationships, business logic) were solved before the UI existed. When I built the frontend, I wasn't guessing at how things should work. The backend already proved it.

If I'd started with the UI, I would've built with assumptions about the backend. Those assumptions would be wrong. I'd spend weeks redesigning the backend, migrating data, rewriting the frontend.

By building bottom-up, I got the foundation right. The frontend integration required some adjustments, but the backend architecture held up without major rewrites.

The Real Benefit: No Rework

Because I built the backend first, I solved the hard problems before the frontend existed.

When I add a new feature, I just add an endpoint. The permission layer already works. The database schema already supports it.

When I build a mobile app, I won't rewrite the backend. I'll just build a new frontend that calls the same API.

Frontend-first developers have to rebuild everything for each platform. I just build new interfaces on top of proven infrastructure.

The Cost

Slower visible progress. For a few weeks, there was no UI to show. Just API endpoints. Just database queries. Just permission logic being tested in isolation.

For a client project with tight deadlines, this would feel slow.

For a product I'm shipping to real users, this felt like investing in the right foundation.

Intentional Decisions: Knowing What to Optimize

The Audio Worker: Measuring and Optimizing

The First Design (Inefficient)

When a user uploads audio, it needs processing:

I initially designed the worker as a cron job: every 5 seconds, scan the database for new uploads, process them.

The Problem

The Realization

The backend already validates every upload. At that moment, it knows a new file exists.

Why scan the database if I can just trigger the worker directly?

The Second Design (Efficient)

Now the flow is:

User uploads audio file

Backend validates permission

Backend saves file to cloud storage

Backend directly calls worker (HTTP request)

Backend returns 200 OK to user (upload complete)

Worker processes in background asynchronously

The Benefits

The Lesson

Don't engineer for theoretical problems. Measure real behavior.

I assumed constant polling was necessary for reliability. It wasn't. By understanding the actual workflow, I found a simpler, cheaper, faster solution.

This kind of optimization only happens when you think through the system before building it.

Security vs. Speed: The False Tradeoff

The Question

"Couldn't you have shipped faster by skipping the backend layer? By trusting the frontend with permissions?"

The Answer

Yes. And it would've been a mistake.

The Trade-off I Actually Made

Backend-first security might take longer to build, but it saved 2-3 weeks of debugging later.

That's not slower. That's faster overall.

When I Did Skip Security

I'm not saying "always maximize security." That's dogmatic.

But permission logic? That's non-negotiable.

Once users trust you with unreleased music worth thousands of dollars, losing that trust is catastrophic. A single permission bug could expose someone's entire catalog.

The Real Insight

Speed and security aren't always opposed. They're opposed if you choose poorly.

If you skip security upfront, you'll spend weeks fixing it later. That's not speed, it's recklessness.

If you build security into the foundation, both your code and your users benefit. That's smart.

What I Learned

Serverless infrastructure isn't about trend-following. It's about knowing your constraints. Solo developers need to focus on product, not ops.

Client-server architecture isn't about being defensive. It's about building systems where permission bugs are architecturally impossible, not just unlikely.

Backend-first development isn't about being slow. It's about building the foundation right so everything else is fast.

Measuring assumptions isn't about overthinking. It's about making one change (polling → direct trigger) and getting 10x cost savings.

Building Uforiya solo forced me to be intentional about every decision. I couldn't hide behind "the team decided." I had to defend every choice.

That accountability produces better architecture.

For Other Developers

If you're building a simple CRUD app, frontend-first might be fine.

If you're building something with real users, real data, real trust, real complexity, think foundational.

Architecture is cheap to change on paper. It's expensive to change in production.

Spend the extra days getting it right upfront.

That's what I learned building Uforiya.

Learn more about Uforiya: