In fast-paced engineering environments, where continuous deployment and distributed ownership are the norm, ensuring system reliability and operational transparency becomes increasingly complex. As organizations move toward microservices architecture to gain agility and scalability, they often trade off simplicity for flexibility. This results in fragmented systems where even the most basic questions become hard to answer:
While teams invest heavily in observability stacks, monitoring dashboards, and alerting systems, these tools often rely on application-level signals that may not exist uniformly across services. The absence of a consistent mechanism to verify the runtime state and metadata of a service leads to longer incident response times, harder debugging, and diminished confidence in automated operations.
That’s why a simple but powerful convention is emerging as a best practice in modern microservices: exposing two standard HTTP endpoints—/version and /health—in every service. These endpoints may seem trivial at first glance, but when implemented thoughtfully and consistently, they unlock critical capabilities around deployment tracking, system health checks, observability, and even automation.
In this post, we’ll explore why these two endpoints matter, how they improve operational confidence, and what best practices to follow when implementing them across your architecture.
While modern observability stacks include dashboards, logs, and traces, these endpoints are effective because of their simplicity. They work across languages, teams, and environments. They don’t require agents or integrations. They don’t depend on vendor tools. And yet, they deliver immediate value.
These two endpoints improve monitoring, deployment verification, and incident response without complicating the codebase.
The /version endpoint typically returns metadata about the running instance: the application version, build number or Git commit SHA, deployment environment, and build timestamp. This information helps developers and operators identify which version of the code is active in any given environment. When incidents occur or anomalies are detected, this metadata makes it easier to correlate behavior with deployments and rollbacks.
The /health endpoint, on the other hand, is a lightweight status check. It usually returns a 200 OK when the service is healthy, and a 500 or non-200 when it's not. It's often integrated with infrastructure components like Kubernetes liveness and readiness probes, service meshes, load balancers, and monitoring tools. A well-implemented health check may include checks for upstream dependencies, database connections, configuration validity, or resource saturation, giving a near real-time signal of whether the service is ready to handle traffic.
Together, these endpoints provide foundational observability across all services. They are simple to implement, language-agnostic, and work seamlessly with automation tools, deployment pipelines, and monitoring systems. Importantly, they encourage consistency, making it easier for platform teams to reason about the state of distributed applications at scale.
The /version endpoint answers a basic but essential question: what is running?
In complex systems, this is not always obvious. Version mismatches between staging and production can cause bugs that are hard to reproduce. Missing deployments or incorrect rollback behavior may go unnoticed. And debugging incidents without knowing the exact running version adds unnecessary friction.
A /version endpoint solves all of this by making build metadata available over HTTP.
Sample output
json
{
"version": "1.3.7",
"build": "qa"
}
This helps platform teams verify deployments, enables monitoring tools to correlate metrics with build versions, and allows developers to confirm that recent changes have reached their target environments.
This pattern is easy to implement in any language or framework.
Java (Micronaut)
java
@Controller("/version")
@Secured(SecurityRule.IS_ANONYMOUS)
public class VersionController {
@Value("${version.ver}")
private String version;
@Value("${version.build}")
private String build;
@Get("/")
public VersionDTO getVersion() {
VersionDTO dto = new VersionDTO();
dto.setVersion(version);
dto.setBuild(build);
return dto;
}
}
This code creates a /version endpoint in a Micronaut application. It reads the version and build values from a configuration file (application.yml) and returns them as a JSON object.
yaml
# application.yml
version:
ver: 1.3.7
build: default
# application-dev.yml
version:
build: dev
These configuration files define environment-specific values. The base file sets a default version and build, while the application-dev.yml overrides the build value in development environments.
Python (FastAPI)
python
from fastapi import FastAPI
app = FastAPI()
@app.get("/version")
def version():
return {
"version": "1.3.7",
"build": "qa"
}
This defines a /version route using FastAPI that returns a hardcoded JSON response with version and build metadata.
Node.js (Express)
javascript
const express = require('express');
const app = express();
app.get('/version', (req, res) => {
res.json({
version: "1.3.7",
build: "qa"
});
});
This Express app sets up a GET endpoint at /version that responds with static version and build information in JSON format.
Go (Gin)
go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/version", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"version": "1.3.7",
"build": "qa",
})
})
r.Run()
}
This Go application uses the Gin framework to expose a /version endpoint. It returns version and build details as a JSON response.
If /version tells us what is running, then /health tells us whether it is working.
The /health endpoint provides a simple status check used by orchestrators, load balancers, and uptime monitors. It can also include deeper diagnostic checks if needed.
Sample output
json
{
"status": "UP"
}
This output is enough for Kubernetes liveness probes, for example. More detailed health endpoints may also include checks on dependencies such as databases or queues.
Java (Micronaut)
groovy
// build.gradle
implementation("io.micronaut:micronaut-management")
This line adds the Micronaut Management dependency, which enables endpoints like /health.
yaml
# application.yml
endpoints:
health:
enabled: true
sensitive: false
This configuration enables the /health endpoint and makes it publicly accessible (not restricted to authenticated users). Once enabled, Micronaut auto-generates the /health endpoint with built-in checks.
Python (FastAPI)
python
@app.get("/health")
def health():
return { "status": "UP" }
Defines a simple HTTP GET route /health in FastAPI. When called, it returns a JSON response indicating the service is running.
Node.js (Express)
javascript
app.get('/health', (req, res) => {
res.json({ status: "UP" });
});
Registers a /health GET route using Express. It responds with JSON to confirm the app is alive.
Go (Gin)
go
r.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "UP",
})
})
This Gin framework snippet creates a /health route that returns a 200 OK response with a status indicator.
These two simple endpoints bring clarity to environments that are otherwise opaque. Their benefits are immediate and compound over time.
Confirm which version is running in every environment at any time
/health works seamlessly with Kubernetes, load balancers, and alerting tools
One convention that applies to all services, regardless of tech stack
Eliminate guesswork in incident response and postmortems
Adding these endpoints does not require refactoring or new dependencies. It can be done incrementally and enforced through templates, CI policies, or review checklists. All new services should include /version and /health from day one to ensure consistency and visibility from the start. For existing services, these endpoints can be introduced gradually during tech-debt cycles or platform upgrade efforts. Continuous Integration (CI) checks can be configured to automatically validate the presence of these endpoints in pull requests, promoting adherence to best practices across teams.
Microservices bring flexibility, but they also add complexity. As systems grow, keeping them observable becomes essential.
The /version and /health endpoints are not groundbreaking. They are not fancy. But they are consistent, proven, and easy to adopt. They reduce blind spots and improve reliability across every environment.
And the best part? They take only a few lines of code.
Let’s keep our systems observable, dependable, and simple. One endpoint at a time.
Expeed Software is a global software company specializing in application development, data analytics, digital transformation services, and user experience solutions. As an organization, we have worked with some of the largest companies in the world, helping them build custom software products, automate processes, drive digital transformation, and become more data-driven enterprises. Our focus is on delivering products and solutions that enhance efficiency, reduce costs, and offer scalability.