MVP за 2 недели: как запустить продукт быстро и не потерять качество.

Практический гайд по запуску MVP за 2 недели: приоритизация фич, выбор стека, день-за-днём план и типичные ошибки, которых стоит избежать.

Почему 2 недели - это идеальный срок для первого запуска

Мы регулярно видим одну и ту же историю. Основатель стартапа приходит с идеей. Команда 6 месяцев разрабатывает продукт: продумывает каждую деталь, добавляет фичу за фичей, полирует дизайн. Тратит бюджет. А после запуска оказывается, что пользователям это не нужно. Или нужно, но совсем по-другому.

6 месяцев работы. Сотни тысяч рублей. И всё мимо.

MVP (Minimum Viable Product - "минимально жизнеспособный продукт") - это противоядие от этой болезни. MVP - это не "кривой прототип" и не "бета-версия с багами". Это минимальный продукт, который решает одну конкретную проблему и позволяет проверить вашу гипотезу на реальных пользователях.

Две недели - это не магическое число, а разумный срок, который заставляет фокусироваться на главном и отсекать лишнее. Если вы не можете запустить MVP за 2 недели - скорее всего, вы пытаетесь сделать слишком много.

День 1: Приоритизация фич (самый важный день)

Первый день - самый критичный. Здесь вы решаете, что войдёт в MVP, а что подождёт. Метод MoSCoW помогает разделить все фичи на 4 категории:

Must have (обязательно) - без этого продукт бессмыслен. Если вы делаете маркетплейс - это каталог товаров, корзина и оформление заказа. Без каталога маркетплейс не маркетплейс.

Should have (важно, но не критично) - добавим через неделю после запуска. Фильтры, поиск, личный кабинет. Продукт может работать без них.

Could have (было бы круто) - добавим, когда появятся пользователи. Рекомендации, отзывы, избранное.

Won't have (не сейчас) - мобильное приложение, программа лояльности, AI-ассистент. Когда-нибудь, но не в MVP.

// Пример: приоритизация для маркетплейса хендмейд-товаров
// Используем метод MoSCoW

const features = {
  // MUST HAVE - без этого продукт не может работать
  // Это и есть ваш MVP. Только это. Ничего больше.
  mustHave: [
    'Каталог товаров с фото и описанием',  // Пользователи видят, что продаётся
    'Страница товара с подробностями',       // Можно рассмотреть товар
    'Корзина',                               // Можно собрать заказ
    'Оформление заказа (имя, телефон, адрес)', // Можно сделать покупку
    'Уведомление продавцу о заказе (Telegram)', // Продавец узнает о заказе
    'Лендинг с объяснением "что это за сайт"',  // Новые посетители понимают суть
  ],

  // SHOULD HAVE - важно, но можно добавить после запуска
  shouldHave: [
    'Регистрация/авторизация покупателей',
    'Личный кабинет продавца (добавление товаров)',
    'Фильтрация по категориям',
    'Поиск по названию',
  ],

  // COULD HAVE - было бы круто, но не критично
  couldHave: [
    'Отзывы о товарах',
    'Избранное (wishlist)',
    'История заказов',
    'Оплата онлайн (Юкасса, Stripe)',
  ],

  // WON'T HAVE - не в этот раз, даже не думаем об этом
  wontHave: [
    'Мобильное приложение',
    'Чат между покупателем и продавцом',
    'Рекомендательная система ("Вам также может понравиться")',
    'Программа лояльности и бонусы',
    'Мультиязычность (английская версия)',
  ],
}

// ПРАВИЛО: если сомневаетесь, куда отнести фичу - относите НИЖЕ
// Лучше запустить с 6 фичами, чем не запустить с 60

День 1 (продолжение): Выбор технологий для скорости

Для MVP выбирайте инструменты, которые дают максимум "из коробки". Не время для экспериментов с новыми фреймворками.

// Рекомендуемый стек для быстрого MVP
// Каждая технология выбрана по одному критерию: скорость разработки

const mvpStack = {
  frontend: 'Next.js 15 (App Router)',
  // Почему: встроенный серверный рендеринг (хорошо для SEO),
  // файловая маршрутизация (создал файл page.tsx = создал страницу),
  // встроенные API-эндпоинты (не нужен отдельный сервер),
  // деплой на Vercel одной кнопкой

  ui: 'Tailwind CSS + shadcn/ui',
  // Почему: shadcn/ui - готовые красивые компоненты (кнопки, формы, модалки)
  // Не нужно писать CSS с нуля. Выглядит профессионально "из коробки"

  database: 'Supabase (PostgreSQL)',
  // Почему: бесплатный тариф для начала,
  // готовая авторизация (Google, email),
  // REST API из коробки (не нужно писать backend для простых CRUD),
  // хранилище файлов (для фото товаров)

  orm: 'Prisma',
  // Почему: пишете TypeScript-код вместо SQL,
  // автоматические миграции базы данных,
  // типобезопасность - меньше ошибок

  deploy: 'Vercel',
  // Почему: сделали git push - сайт обновился. Бесплатно для малых проектов

  analytics: 'Yandex Metrika',
  // Почему: бесплатно, вебвизор (записи экрана), карта кликов.
  // Понимаете, что делают пользователи на сайте
}

День 2: Схема базы данных

Для MVP схема должна быть минимальной. Не проектируйте "на вырост" - это ловушка, из-за которой MVP превращается в 6-месячный проект.

// prisma/schema.prisma - схема базы данных для маркетплейса
// Prisma - ORM (Object-Relational Mapping)
// Вместо SQL вы описываете структуру данных в простом формате

// Настройки Prisma
generator client {
  provider = "prisma-client-js"  // Генерировать TypeScript-клиент
}

datasource db {
  provider = "postgresql"         // Используем PostgreSQL
  url      = env("DATABASE_URL")  // Адрес базы данных из переменной окружения
}

// model - описание таблицы в базе данных
// Каждый model = одна таблица

// Пользователи (и покупатели, и продавцы)
model User {
  id        String   @id @default(cuid())  // Уникальный ID (генерируется автоматически)
  email     String   @unique                // Email (не может повторяться)
  name      String                          // Имя
  role      Role     @default(BUYER)        // Роль: покупатель или продавец
  products  Product[]                       // Список товаров (если продавец)
  orders    Order[]                         // Список заказов (если покупатель)
  createdAt DateTime @default(now())        // Дата регистрации
}

// enum - перечисление возможных значений
enum Role {
  BUYER    // Покупатель
  SELLER   // Продавец
  ADMIN    // Администратор
}

// Товары
model Product {
  id          String   @id @default(cuid())
  title       String                         // Название товара
  description String                         // Описание
  price       Int                            // Цена в КОПЕЙКАХ (не рублях!)
  // Почему в копейках? Потому что числа с плавающей точкой (Float)
  // дают ошибки: 0.1 + 0.2 = 0.30000000000000004
  // А целые числа (Int) точные: 100 + 200 = 300 (копеек = 3 рубля)
  images      String[]                       // Массив ссылок на фото
  category    String                         // Категория
  seller      User     @relation(fields: [sellerId], references: [id])
  sellerId    String                         // ID продавца
  active      Boolean  @default(true)        // Товар активен (виден в каталоге)
  orderItems  OrderItem[]                    // В каких заказах есть этот товар
  createdAt   DateTime @default(now())
}

// Заказы
model Order {
  id         String      @id @default(cuid())
  buyer      User        @relation(fields: [buyerId], references: [id])
  buyerId    String
  items      OrderItem[]                     // Список товаров в заказе
  total      Int                             // Общая сумма (в копейках)
  status     OrderStatus @default(NEW)       // Статус заказа
  name       String                          // Имя получателя
  phone      String                          // Телефон
  address    String                          // Адрес доставки
  createdAt  DateTime    @default(now())
}

// Позиция заказа (конкретный товар в заказе)
model OrderItem {
  id        String  @id @default(cuid())
  order     Order   @relation(fields: [orderId], references: [id])
  orderId   String
  product   Product @relation(fields: [productId], references: [id])
  productId String
  quantity  Int                              // Количество
  price     Int                              // Цена НА МОМЕНТ ЗАКАЗА
  // Почему цена дублируется? Потому что продавец может изменить цену
  // после заказа, но заказ должен сохранить оригинальную цену
}

// Статусы заказа
enum OrderStatus {
  NEW        // Новый (только оформлен)
  CONFIRMED  // Подтверждён продавцом
  SHIPPED    // Отправлен
  DELIVERED  // Доставлен
  CANCELLED  // Отменён
}

План по дням: две недели от идеи до запуска

Дни 1-2: Фундамент

  • Приоритизация фич (MoSCoW) - что входит в MVP
  • Создание проекта (Next.js + Prisma + Supabase)
  • Схема базы данных + первая миграция
  • Базовый layout (шапка, навигация, подвал - общий каркас)
  • Авторизация (вход через email + Google)

Дни 3-5: Ядро продукта (самое важное)

  • Каталог товаров (список с карточками)
  • Страница товара (фото, описание, цена, кнопка "В корзину")
  • Форма добавления товара для продавцов
  • Загрузка фото (Supabase Storage)

Дни 6-8: Заказы

  • Корзина (хранится в localStorage - не нужна авторизация для добавления)
  • Страница оформления заказа (имя, телефон, адрес)
  • Уведомление продавцу (Telegram-бот или email)
  • Страница "Спасибо за заказ"

Дни 9-10: Лендинг и SEO

  • Главная страница: заголовок, преимущества, призыв к действию
  • Мета-теги для поисковиков (title, description)
  • Open Graph (красивые превью в соцсетях)
  • sitemap.xml и robots.txt

Дни 11-12: Полировка

  • Мобильная версия (адаптивная вёрстка)
  • Страницы ошибок (404, 500)
  • Пустые состояния ("Каталог пуст", "Корзина пуста")
  • Loading-скелетоны (индикаторы загрузки)

Дни 13-14: Запуск!

  • Деплой на Vercel (или VPS)
  • Подключение домена
  • SSL-сертификат (HTTPS - бесплатный от Let's Encrypt)
  • Аналитика (Yandex Metrika)
  • Тест на реальных людях (5-10 друзей/знакомых)
  • Исправление критичных багов

Авторизация за 30 минут

С NextAuth.js настройка авторизации - буквально один файл. Вход через Google и по email (magic link - пользователь получает ссылку на почту):

// src/app/api/auth/[...nextauth]/route.ts
// Настройка авторизации с NextAuth.js

import NextAuth from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
import EmailProvider from 'next-auth/providers/email'
import { PrismaAdapter } from '@auth/prisma-adapter'
import { prisma } from '@/lib/prisma'

const handler = NextAuth({
  // PrismaAdapter - автоматически сохраняет пользователей в вашу БД
  adapter: PrismaAdapter(prisma),

  providers: [
    // Вход через Google
    // Для этого нужно зарегистрировать приложение в Google Cloud Console
    // и получить clientId и clientSecret
    GoogleProvider({
      clientId: process.env.GOOGLE_ID!,
      clientSecret: process.env.GOOGLE_SECRET!,
    }),

    // Вход по email (magic link)
    // Пользователь вводит email, получает ссылку, кликает - и он авторизован
    EmailProvider({
      server: process.env.EMAIL_SERVER,  // SMTP-сервер для отправки писем
      from: process.env.EMAIL_FROM,      // От чьего имени ([email protected])
    }),
  ],

  pages: {
    signIn: '/auth/signin',  // Кастомная страница входа
  },
})

export { handler as GET, handler as POST }

// Использование в любом серверном компоненте:
// import { getServerSession } from 'next-auth'
// const session = await getServerSession()
// if (!session) redirect('/auth/signin')
// console.log(session.user.email) // email авторизованного пользователя

5 главных ошибок при создании MVP

Ошибка 1: Overengineering (переусложнение)

"Нам нужна микросервисная архитектура, Kubernetes, очереди сообщений и CI/CD с 50 шагами". Нет. Вам нужен ОДИН сервер и ОДНА база данных. Масштабировать будете, когда придут пользователи. А чтобы они пришли - нужно сначала запуститься.

Ошибка 2: Идеальный дизайн

"Дизайнер ещё не утвердил третий вариант кнопки". Shadcn/ui + Tailwind дают профессионально выглядящий интерфейс за минуты. Для MVP этого более чем достаточно. Кастомный дизайн от дизайнера - после того, как убедитесь, что продукт нужен.

Ошибка 3: Фичи, которые никто не просил

"Давайте сразу добавим чат, рейтинги, программу лояльности и рекомендации". Каждая лишняя фича - это дни разработки, новые баги и будущая поддержка. Запустите с минимумом, послушайте реальных пользователей, добавляйте то, что они просят.

Ошибка 4: Преждевременная оптимизация

"Нужно сделать кэширование Redis, потому что будет 100 000 пользователей". У вас пока 0 пользователей. PostgreSQL на минимальном сервере выдержит тысячи запросов в секунду. Оптимизируйте, когда появится реальная проблема, а не воображаемая.

Ошибка 5: Нет аналитики с первого дня

Это единственное, что НЕЛЬЗЯ отложить. Без аналитики вы слепы: не знаете, кто приходит на сайт, что делает, где уходит. Yandex Metrika - 5 минут на установку, бесплатно, вебвизор (запись экрана) показывает каждое действие пользователя. Добавьте события на ключевые действия: регистрация, добавление в корзину, оформление заказа.

Лендинг: как объяснить посетителю, зачем ему ваш продукт

MVP без лендинга - это продукт, о котором никто не узнает. Минимальная структура:

  1. Hero - главный экран: что это (заголовок), для кого (подзаголовок), кнопка "Попробовать бесплатно"
  2. Проблема - опишите боль вашего пользователя (2-3 предложения)
  3. Решение - как ваш продукт решает эту боль (скриншоты, видео)
  4. Преимущества - 3-4 ключевых фичи с иконками
  5. Социальное доказательство - отзывы, цифры, логотипы партнёров (если есть)
  6. CTA (призыв к действию) - ещё одна кнопка "Начать сейчас" или "Оставить заявку"

После запуска: что делать в первую неделю

MVP запущен. Поздравляем! Теперь начинается самое интересное:

  • Дни 1-2: Мониторьте ошибки. Если есть Sentry - смотрите отчёты. Если нет - хотя бы смотрите логи сервера. Исправляйте критичные баги
  • Дни 3-5: Смотрите аналитику. Откуда приходят пользователи? На какой странице уходят? Какие кнопки нажимают? Yandex Metrika покажет всё
  • Дни 5-7: Поговорите с 5-10 первыми пользователями. Что неудобно? Чего не хватает? Что непонятно? Эта обратная связь на вес золота
  • По результатам - планируйте следующий спринт (ещё 2 недели). Добавляйте фичи из "Should have", которые реально просят пользователи

API за 15 минут: как сделать корзину и оформление заказа

Корзина - это одна из Must Have фич для любого магазина. Вот минимальная реализация, которую можно сделать за 15 минут:

// src/app/api/orders/route.ts
// API-endpoint для создания заказа
// Next.js App Router - каждый файл route.ts автоматически становится API

import { prisma } from '@/lib/prisma'
import { NextRequest, NextResponse } from 'next/server'

// POST /api/orders - создать новый заказ
export async function POST(request: NextRequest) {
  try {
    // Получаем данные из тела запроса
    const body = await request.json()
    const { items, name, phone, address } = body

    // Проверяем обязательные поля
    // Без имени и телефона мы не сможем связаться с покупателем
    if (!items || items.length === 0) {
      return NextResponse.json(
        { error: 'Корзина пуста' },
        { status: 400 }
      )
    }

    if (!name || !phone) {
      return NextResponse.json(
        { error: 'Укажите имя и телефон' },
        { status: 400 }
      )
    }

    // Получаем актуальные цены товаров из базы данных
    // ВАЖНО: не доверяйте ценам от клиента!
    // Пользователь может изменить цену в DevTools браузера
    const productIds = items.map((item: any) => item.productId)
    const products = await prisma.product.findMany({
      where: { id: { in: productIds } }
    })

    // Считаем реальную сумму заказа
    let total = 0
    const orderItems = items.map((item: any) => {
      const product = products.find(p => p.id === item.productId)
      if (!product) throw new Error(`Товар ${item.productId} не найден`)

      total += product.price * item.quantity
      return {
        productId: product.id,
        quantity: item.quantity,
        price: product.price,  // Цена из базы, не от клиента!
      }
    })

    // Создаём заказ в базе данных
    const order = await prisma.order.create({
      data: {
        name,
        phone,
        address: address || '',
        total,
        items: {
          create: orderItems  // Prisma создаст все OrderItem автоматически
        }
      },
      include: { items: true }  // Вернуть заказ вместе с товарами
    })

    // Отправляем уведомление продавцу в Telegram
    // (Telegram-бот настраивается за 10 минут через @BotFather)
    await sendTelegramNotification(
      `Новый заказ #${order.id}!\n` +
      `Имя: ${name}\n` +
      `Телефон: ${phone}\n` +
      `Сумма: ${(total / 100).toFixed(2)} руб.\n` +
      `Товаров: ${orderItems.length} шт.`
    )

    return NextResponse.json({ data: order }, { status: 201 })
  } catch (error) {
    console.error('Ошибка создания заказа:', error)
    return NextResponse.json(
      { error: 'Ошибка сервера' },
      { status: 500 }
    )
  }
}

// Функция отправки уведомления в Telegram
async function sendTelegramNotification(text: string) {
  const botToken = process.env.TELEGRAM_BOT_TOKEN
  const chatId = process.env.TELEGRAM_CHAT_ID

  if (!botToken || !chatId) return  // Если не настроено - пропускаем

  await fetch(
    `https://api.telegram.org/bot${botToken}/sendMessage`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ chat_id: chatId, text }),
    }
  )
}

Корзина на фронтенде: без базы данных

Для MVP корзину можно хранить в localStorage (локальное хранилище браузера). Это позволяет добавлять товары без авторизации - очень удобно для пользователей:

// lib/cart.ts - работа с корзиной через localStorage
// localStorage - хранилище в браузере, данные сохраняются даже после закрытия вкладки

// Тип для элемента корзины
interface CartItem {
  productId: string  // ID товара
  name: string       // Название (для отображения)
  price: number      // Цена (в копейках)
  quantity: number    // Количество
  image: string      // Фото (URL)
}

// Получить все товары из корзины
export function getCart(): CartItem[] {
  // Читаем из localStorage
  // localStorage хранит только строки, поэтому JSON.parse
  const data = localStorage.getItem('cart')
  return data ? JSON.parse(data) : []
}

// Добавить товар в корзину
export function addToCart(product: CartItem) {
  const cart = getCart()

  // Проверяем, есть ли уже этот товар в корзине
  const existing = cart.find(item => item.productId === product.productId)

  if (existing) {
    // Если есть - увеличиваем количество
    existing.quantity += 1
  } else {
    // Если нет - добавляем с количеством 1
    cart.push({ ...product, quantity: 1 })
  }

  // Сохраняем обратно в localStorage
  localStorage.setItem('cart', JSON.stringify(cart))
}

// Удалить товар из корзины
export function removeFromCart(productId: string) {
  const cart = getCart().filter(item => item.productId !== productId)
  localStorage.setItem('cart', JSON.stringify(cart))
}

// Изменить количество товара
export function updateQuantity(productId: string, quantity: number) {
  const cart = getCart()
  const item = cart.find(i => i.productId === productId)
  if (item) {
    item.quantity = Math.max(1, quantity)  // Минимум 1
  }
  localStorage.setItem('cart', JSON.stringify(cart))
}

// Посчитать общую сумму
export function getCartTotal(): number {
  return getCart().reduce(
    (sum, item) => sum + item.price * item.quantity,
    0
  )
}

// Очистить корзину (после оформления заказа)
export function clearCart() {
  localStorage.removeItem('cart')
}

Реальная стоимость MVP: сколько это стоит

Вот честный расчёт затрат на MVP маркетплейса за 2 недели:

Если делаете сами (0 руб + ваше время):

  • Vercel (хостинг) - бесплатный тариф
  • Supabase (база данных) - бесплатный тариф (500 MB, 50 000 запросов)
  • Домен (.ru) - 200-500 руб/год
  • SSL-сертификат - бесплатно (Let's Encrypt или Vercel)
  • Yandex Metrika - бесплатно
  • Итого: 200-500 руб + 2 недели вашего времени

Если заказываете у нас:

  • Проектирование + дизайн + разработка + деплой
  • Настройка аналитики и SEO
  • Обучение работе с админ-панелью
  • 3 месяца гарантийной поддержки
  • От 150 000 руб за полный MVP
  • Вы экономите 2 недели своего времени и получаете профессиональный продукт

Итого

MVP за 2 недели - это реально, если фокусироваться на главном. Приоритизируйте жёстко (MoSCoW), выбирайте стек для скорости (Next.js + Supabase + Prisma), не поддавайтесь соблазну "а давайте ещё добавим...".

Цель MVP - не идеальный продукт, а проверенная гипотеза. Если идея подтвердилась реальными пользователями - масштабируйте. Если нет - вы потеряли 2 недели, а не 6 месяцев. В любом случае вы в плюсе.

Нужна помощь с разработкой MVP? Напишите нам - мы специализируемся на быстром запуске продуктов. Первая консультация бесплатно.

Все статьи
MVP за 2 недели: как запустить продукт быстро и не потерять качество | Enot Software