From Vision to Iteration: The Evolution of Our User Registration Flow

At Tutorial Haven, our mission has always been to bridge the gap between education and technology. What began as a multi-tiered platform for schools, tutors, and students has evolved through real-world feedback and strategic refinement. Today, I'm pulling back the curtain on how our registration system transformed—and what the journey taught me about building for scale.

🎯 The Original Vision: Building for Everyone

When I first built Tutorial Haven, I didn't design it as "just another student app." I envisioned it being adopted by schools and tutorial centers to enhance teaching and learning workflows. So registration wasn't simple. It was split into three distinct flows—each with its own logic, validation, and access controls.

Here's a look at how our registration system was initially built: I designed the platform with scalability in mind, envisioning adoption by schools, tutorial centers, tutors, and students alike. The registration was structured into three distinct flows:

🏫 1. School/Institution Registration

Fields Required:
  • First name, last name, username, password, email
  • Tutorial center name, address, discipline (e.g., JAMB, WAEC, JUPEB)
  • Profile picture (optional)

Process:

  • Basic validation (unique username, email, and center name)
  • Profile picture uploaded via SupabaseStorage() with URL saved to the tutorial center model
  • Creation of both the user and tutorial center objects
  • Redirect to the user's profile page upon success

👨‍🏫 2. Tutor Registration

Fields Required:
  • Basic user info + selection of institution and courses (16 available)
  • Profile picture

Process:

  • Same validation and image upload flow
  • Key Feature: Tutors were linked to their institution, and the center owner received an email notification to approve or reject the registration
  • Tutors had no platform access until approved—ensuring quality and oversight

👩‍🎓 3. Student Registration

Fields Required:
  • Department (Science, Art, Commercial), institution, subjects
  • Basic user info + phone number (for platform growth insights)

Process:

  • Validation, profile upload, user + student model creation
  • Email sent to both the institution owner and student
  • Access granted only after approval

Each flow included validation checks, profile image uploads, email notifications, and role-based access control. It was comprehensive. It was scalable. And honestly? It was probably too much.

🔄 The Evolution: What Changed and Why

Over time, I removed the owner and tutor registration flows (the reasoning will be shared soon), but the architecture taught me something important:

💡 The Lesson

Designing for scale early forces you to think beyond "just making it work." But there's a fine line between scalable architecture and over-engineering for problems you don't yet have.

Here's what prompted the changes:

🏗️ Technical Deep Dive: What the Code Looked Like

For the curious developers, here's a simplified version of how we handled the multi-flow registration with Supabase:

// Multi-flow registration handler (simplified)
async function handleRegistration(userData, userType) {
  try {
    // 1. Basic validation
    await validateUser(userData.email, userData.username);
    
    // 2. Upload profile picture if exists
    let profileUrl = null;
    if (userData.profileImage) {
      const { data, error } = await supabase.storage
        .from('profiles')
        .upload(`${userType}/${Date.now()}.jpg`, userData.profileImage);
      profileUrl = data?.publicUrl;
    }
    
    // 3. Create auth user
    const { data: authUser, error: authError } = await supabase.auth.signUp({
      email: userData.email,
      password: userData.password,
    });
    
    // 4. Create role-specific profile
    const profileData = {
      user_id: authUser.user.id,
      first_name: userData.firstName,
      last_name: userData.lastName,
      profile_url: profileUrl,
      ...getRoleSpecificFields(userData, userType)
    };
    
    // 5. Insert into appropriate table
    const { error: profileError } = await supabase
      .from(`${userType}_profiles`)
      .insert(profileData);
      
    // 6. Send notifications based on role
    await sendRoleBasedNotifications(userType, profileData);
    
    return { success: true };
  } catch (error) {
    console.error('Registration failed:', error);
    return { success: false, error };
  }
}

📊 What I'd Do Differently Today

If I were rebuilding this system from scratch with today's knowledge:

  1. Start simpler: One registration flow with role assignment after email verification
  2. Progressive onboarding: Collect minimal info first, then gather role-specific details after initial value is delivered
  3. Feature flags: Build the architecture for multiple roles, but release them gradually based on demand
  4. Analytics from day one: Track where users drop off in each flow to identify friction points

⚡ Pro Tip for Fellow Builders

Your first architecture should solve today's problems elegantly while leaving hooks for tomorrow's complexity—not implementing it all upfront. Ask yourself: "What's the simplest thing that could possibly work for our current users?"

🔮 What's Next for Tutorial Haven

The registration flow continues to evolve. We're currently experimenting with:

I built all this quietly. Now I'm finally documenting it—one feature at a time.

📝 Key Takeaway

The best architectures aren't designed perfectly from the start—they evolve through real usage, honest feedback, and the courage to simplify. Tutorial Haven's registration flow taught me that scalability isn't just about handling more users; it's about adapting to what they actually need.