This is post #10 in our How It Works series. Our running example is building a task management app — a simplified Trello for small dev teams. We have gone through understanding the problem, planning, wireframing, designing, implementing, and testing. Now it is time to ship something real.
An MVP — Minimum Viable Product — is the smallest version of your product that delivers value to real users. It is not a half-baked product. It is a focused product. You strip away everything that is not essential and ship only the core that solves the problem.
The concept comes from Eric Ries and the Lean Startup methodology. Instead of spending months building a fully featured product that nobody asked for, you build the minimum that lets you test whether your solution actually works in the real world. Then you measure, learn, and iterate.
The key word is viable. An MVP must work. It must be stable enough for real users in production. It must solve at least one problem well enough that someone would choose to use it.
Engineers confuse these constantly. A prototype tests feasibility — can we build this? Does the technology work? A prototype lives on your laptop or in a demo environment. Nobody depends on it.
An MVP tests market fit — do users want this? Will they use it? An MVP goes to real users in production. It has authentication, error handling, and monitoring. It might be small, but it is production-grade.
For our task app, a prototype might be a CLI script that creates tasks in a JSON file. The MVP is a deployed web application with a login page, a database, and an API that real team members use every day.
This is where discipline matters. You will be tempted to add features. Resist. Look at your user stories from earlier in the series and pick only the ones that address the core problem.
For our task management app, the MVP scope is:
That is it. No drag-and-drop Kanban boards. No Slack notifications. No GitHub integration. No file attachments. Every feature you cut from the MVP is a week you ship sooner.
Write your MVP scope down and tape it to your monitor. When someone says “wouldn’t it be cool if…” point at the list. If it is not on the list, it is not in the MVP.
An MVP that is not deployed is just a prototype with ambition. Get it in front of users. You need three things: deployment, infrastructure, and monitoring.
Keep deployment simple. A single server, a managed database, and a reverse proxy is enough. Do not build a Kubernetes cluster for ten users. A Spring Boot application with a health check and metrics endpoint gives you the operational visibility you need from day one:
@RestController
public class HealthController {
private final AtomicLong tasksCreated = new AtomicLong(0);
private final AtomicLong tasksCompleted = new AtomicLong(0);
@GetMapping("/health")
public Map<String, Object> health() {
Map<String, Object> status = new HashMap<>();
status.put("status", "UP");
status.put("timestamp", Instant.now().toString());
status.put("version", "1.0.0-mvp");
return status;
}
@GetMapping("/metrics")
public Map<String, Object> metrics() {
Map<String, Object> m = new HashMap<>();
m.put("tasks_created_total", tasksCreated.get());
m.put("tasks_completed_total", tasksCompleted.get());
m.put("uptime_ms", ManagementFactory
.getRuntimeMXBean().getUptime());
return m;
}
public void recordTaskCreated() {
tasksCreated.incrementAndGet();
}
public void recordTaskCompleted() {
tasksCompleted.incrementAndGet();
}
}
The /health endpoint tells you the app is alive. The /metrics endpoint tells you whether anyone is using it. Here is the same concept in Flask:
import time
import logging
from flask import Flask, jsonify
app = Flask(__name__)
start_time = time.time()
metrics = {
"tasks_created": 0,
"tasks_completed": 0,
"errors": 0
}
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s"
)
logger = logging.getLogger(__name__)
@app.route("/health")
def health():
return jsonify({
"status": "UP",
"version": "1.0.0-mvp",
"uptime_seconds": round(time.time() - start_time, 2)
})
@app.route("/metrics")
def get_metrics():
return jsonify(metrics)
def record_task_created():
metrics["tasks_created"] += 1
logger.info("Task created. Total: %d", metrics["tasks_created"])
def record_task_completed():
metrics["tasks_completed"] += 1
logger.info("Task completed. Total: %d", metrics["tasks_completed"])
Both examples follow the same principle: expose the minimum operational data you need to know if the system is healthy and if users are engaging with it.
You shipped. Now pay attention. An MVP without measurement is just guessing with extra steps. Track these metrics from day one:
You do not need a fancy analytics platform. The metrics endpoints above, combined with basic logging, will tell you what you need to know. If you want more, add a lightweight tool like PostHog. Avoid building a custom analytics system — that is scope creep.
Data tells you what is happening. Users tell you why. You need both.
Talk to your users now, while they are using the MVP. Ask three questions:
Combine qualitative feedback with your metrics. If users say they love the app but daily active users are declining, something is wrong that they are not telling you. If signups are strong but task creation is low, the onboarding flow is broken.
This is the Build-Measure-Learn loop from Lean Startup in practice. Every cycle makes the product better — not because you guessed what users wanted, but because you watched them use it and listened.
“If you’re not embarrassed by the first version of your product, you’ve launched too late.” — Reid Hoffman, co-founder of LinkedIn.
This is not an excuse to ship garbage. It is a reminder that perfect is the enemy of shipped. Your MVP will have rough edges. The design will not be polished. There will be features you wish you had included. That is the point. You are not building the final product — you are building the first product that lets you learn what the final product should be.
Ship small. Measure everything. Listen to users. In the next post, we cover Iterating — where we take everything we learned from the MVP and build the next version.