La interfaz está localizada; las descripciones y documentos de las Skills se traducen automáticamente con IA a su idioma. El código, los comandos, rutas y términos técnicos se mantienen en el original, y prevalece el documento del autor.
Explorar Skills
Despliegue y operaciones · SKILL.md

runtime-ecosystem-selection

runtime-ecosystem-selection

Selects the optimal runtime ecosystem (JVM, .NET, Node.js/V8, native compilation) based on deployment characteristics, performance requirements, and operational constraints for production systems.

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentación de la Skill

GitHub

name: runtime-ecosystem-selection description: Selects the optimal runtime ecosystem (JVM, .NET, Node.js/V8, native compilation) based on deployment characteristics, performance requirements, and operational constraints for production systems. license: MIT compatibility: opencode archetypes:

  • tactical
  • strategic
  • diagnostic anti_triggers:
  • brainstorming
  • vague ideation
  • code golf response_profile: verbosity: medium directive_strength: high abstraction_level: tactical metadata: version: "1.0.0" domain: coding triggers: runtime selection, JVM vs .NET, container image size, serverless cold start, GraalVM Native Image, .NET Native AOT, startup time, memory footprint role: implementation scope: implementation output-format: analysis content-types: [code, guidance, do-dont, examples] related-skills: modern-language-comparison, polyglot-development, framework-selection, version-migration

Runtime Ecosystem Selection

Evaluates and selects the optimal runtime ecosystem — JVM (Java/Kotlin), .NET, Node.js/V8, or native compilation (Rust/Go) — by analyzing deployment constraints, cold start requirements, memory footprints, operational maturity, and infrastructure alignment. Translates business and technical requirements into a concrete runtime recommendation with quantified trade-offs.

TL;DR Checklist

  • Quantify cold start budget before selecting any runtime — if <500ms, native or Node.js only
  • Compare container image sizes across runtimes for the same application surface area
  • Verify JIT warm-up impact on autoscaling behavior for JVM/.NET workloads
  • Assess reflection/dynamic code loading needs — blocks .NET Native AOT and GraalVM readiness
  • Check operational tooling maturity (debugging, profiling, observability) for each candidate runtime
  • Run serverless cold start benchmarks with realistic payload before committing to a runtime

When to Use

Use this skill when:

  • Making an architecture decision about the primary execution runtime for a new service or platform migration
  • Evaluating whether to containerize a Java/.NET application or compile to native binary for edge/serverless deployment
  • Comparing cold start impact across runtimes for serverless or autoscaling workloads
  • Assessing memory and CPU budget constraints for high-density container deployments (e.g., 100+ replicas)
  • Migrating an existing service from one runtime to another and need quantified trade-off analysis
  • Selecting between JVM, .NET, Node.js, or native for a greenfield microservice with specific deployment targets

When NOT to Use

Avoid this skill for:

  • Comparing individual programming languages (e.g., "should I use Rust or Go?") — use modern-language-comparison instead
  • Designing cross-language integration and interop patterns within a monorepo — use polyglot-development instead
  • Evaluating frameworks within a single runtime (e.g., Spring Boot vs Quarkus, ASP.NET Core vs FastAPI) — use framework-selection instead
  • Planning version upgrades or breaking changes for an existing runtime stack — use version-migration instead
  • Selecting languages based purely on developer team expertise with no deployment constraint pressure

Core Workflow

  1. Collect Deployment Requirements — Elicit hard constraints from stakeholders: cold start budget, memory-per-request limits, expected request volume, autoscaling behavior, regulatory requirements, and target infrastructure (Kubernetes, Lambda, bare metal, edge). Checkpoint: Every requirement must have a numeric threshold where possible (e.g., "p95 latency < 200ms" not "fast enough").

  2. Score Each Runtime Against Requirements — Evaluate JVM, .NET, Node.js, and native compilation against each quantified constraint using the reference data in the Runtime Comparison Table. Weight scores by business priority (cold start, footprint, throughput, operational maturity). Checkpoint: No runtime may be recommended unless it passes every hard requirement with zero tolerance exceptions.

  3. Run Practical Benchmarks — Build a minimal representative application in each candidate runtime and measure: container image size, cold start latency (10 iterations), warm-up time to steady-state throughput, memory under load, and garbage collection pause impact on p99. Checkpoint: Benchmark infrastructure must be identical across runs (same CPU type, same network, same OS version).

  4. Assess Operational Readiness — For each runtime that survives quantitative scoring, evaluate: debugging tooling availability (profilers, APM), dependency update frequency and breakage rate, security CVE response SLA, container orchestration compatibility, and team competency. Checkpoint: If no runtime has adequate operational tooling for the target environment, flag as a blocking risk before proceeding to recommendation.

  5. Produce Recommendation with Trade-off Matrix — Present the selected runtime alongside explicit trade-offs against the closest alternative, including quantified differences in every measured dimension and a migration plan if applicable. Checkpoint: The recommendation must include a rollback trigger (e.g., "if p99 latency exceeds X under production load, revert to [alternative]").


Runtime Comparison Reference Table

The following table provides current (2025-2026) data for the four primary runtime ecosystems. Values are typical ranges for a representative HTTP service with ~50 endpoints; actual numbers vary by application complexity and optimization level.

DimensionJVM (Java 21+/Kotlin 2.x).NET (.NET 8+/9+)Node.js/V8 (Node 20/22, TS 5.6+)Native Compilation (Rust 1.83+, Go 1.23+)
Minimum heap footprint100–300 MB50–150 MB30–80 MB per instance< 10 MB (static binary)
Cold start (cold container)2–8 seconds1–4 seconds (JIT)< 500 milliseconds< 50 milliseconds
Cold start (.NET Native AOT)N/A< 50 milliseconds (ASP.NET Core)N/A< 10 milliseconds
GraalVM Native Image3–8 seconds build, < 100ms cold startN/AN/AN/A
JIT warm-up time2–5 minutes to steady-state throughput1–3 minutes to steady-stateImmediate (V8 optimizes per-request)No JIT — always at peak performance
p99 latency under load5–20 ms (after warm-up, GC pauses can spike)3–15 ms after warm-up2–10 ms for I/O-bound; CPU-heavy degrades fast1–5 ms consistently
Container image size (optimized)150–300 MB (distroless + JRE slim)80–150 MB (mcr.microsoft.com/dotnet:aspnet-runtime-nanoserver-alpine equivalent)40–80 MB (alpine-based Node image)5–20 MB (static binary in scratch/Alpine)
GC impact on p99Predictable with ZGC/Shenandoah; still 1-3ms pauses under pressureServer GC is low-latency; Gen2 collections can cause 5-20ms stallsV8 marks-sweep: < 1ms typical, spikes to 10ms under allocation pressureZero — no garbage collector
Autoscaling responsivenessSlow — new pods need JIT warm-up before serving meaningful trafficModerate — JIT startup ~1s; Native AOT nearly instantFast — V8 startup < 200ms per instanceInstant — static binary loads immediately
Reflection / dynamic code loadingFull support — core framework featureLimited in Native AOT; reflection requires --EnableDynamicAssemblyLoadingFirst-class (dynamic typing, eval, runtime code generation)No reflection or dynamic loading by design
Dependency update cadence6 months (LTS); security patches as needed6 months (LTS + current); frequent patch releasesEvery ~6 weeks per release line; LTS stable for 12+ monthsRust: every 6 weeks; Go: every 6 months
Security CVE response SLAOracle: 2 weeks critical; OpenJDK community variesMicrosoft: < 48 hours for critical .NET runtime CVEsnpm ecosystem variable (Node.js core: ~1 week)Rust/Go: 2-4 weeks for stdlib, crates/go modules vary
Profiling / APM maturityExcellent — VisualVM, JFR, Async Profiler, Datadog, New RelicGood — dotnet-counters,dotnet-dump, Perfolizer, OpenTelemetryModerate — Clinic.js, 0x, PM2; fewer enterprise profilersGrowing — pprof (Go), perf/bpftrace (Rust); limited APM
Best deployment fitLong-running stateless/stateful services, enterprise platformsEnterprise APIs, high-throughput web services, Azure-first orgsEvent-driven microservices, I/O-bound workloads, full-stack JS teamsEdge computing, serverless functions, containers at extreme scale, compliance-sensitive deployments
Worst deployment fitServerless with sub-second cold start budgets, edge devicesEnvironments requiring zero-reflection or maximum native image reductionCPU-intensive computation, long-lived connections without request turnoverTeams needing rapid prototyping with dynamic iteration cycles

Decision Matrix

Use this matrix to quickly narrow candidates based on the dominant constraint:

Dominant ConstraintPrimary ChoiceAlternativeRationale
Sub-second cold start mandatoryNative (Rust/Go) or Node.js.NET Native AOTJIT runtimes always have measurable startup cost
< 50 MB container image budgetRust static binary in scratchGo, Node.js AlpineJVM/.NET cannot produce images under ~80 MB reliably
Maximum throughput at steady stateJVM (ZGC) or .NET Server GCGo for simplicityMature GCs with parallel collection handle massive RPS
I/O-bound event-driven architectureNode.js or GoRust for safety-criticalEvent loop model minimizes thread-per-request overhead
Zero-reflection requirementNative (Rust/Go).NET Native AOT (with limitations)JVM requires reflection for core frameworks (Spring, Hibernate)
Rapid iteration during developmentNode.js/TypeScript or Python on JVMC# with hot-reloadJIT + dynamic typing enables fastest dev→deploy cycle
Regulatory / compliance audit trail.NET (Microsoft SLA) or Java (Oracle SLA)Go (simpler attack surface)Enterprise vendor support contracts provide compliance documentation
Edge deployment / tiny devicesRust or Go static binaryNode.js with sharp runtime trimmingNo OS dependencies, single file deploy

Implementation Patterns

Pattern 1: Native Compilation Configuration — GraalVM vs .NET Native AOT

Both Java and C# now support ahead-of-time native compilation. This pattern demonstrates how to configure both for production deployment, including the trade-offs each imposes.

GraalVM Native Image (Java/Kotlin):

// build.gradle.kts — Quarkus with GraalVM Native Image plugin
plugins {
    id("io.quarkus") version "3.17.5"
}

quarkus {
    graalvmNative {
        javaCompiler = "graalvm-javac"
        buildArgs.addAll(
            "-H:+UnlockExperimentalVMOptions",
            "-H:IncludeResourceBundles=com.sun.security.ntlmprovider",
            "-Dquarkus.native.enable-all-charsets=true",
            // Reduce image size by excluding unused JDK features
            "--initialize-at-build-time=org.slf4j",
            "-H:+ReportExceptionStackTraces"
        )
        // Native Image reflection config for frameworks needing runtime registration
        metadataCopyResources {
            from(layout.projectDirectory.dir("src/main/resources/META-INF/native-image"))
            into("META-INF/native-image")
        }
    }
}

// src/main/resources/META-INF/native-image/com.example/app/reflect-config.json
[
  {
    "name": "com.example.api.dto.ResponseDto",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "allDeclaredFields": true,
    "allPublicFields": true
  }
]

Dockerfile — GraalVM Native Image build:

# Multi-stage build: Maven download → GraalVM native-image compile → minimal runtime
FROM ghcr.io/graalvm/native-image:21 as builder

WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN native-image \
    --class com.example.Application \
    -H:Name=app-server \
    --no-fallback \
    -O3 \
    -H:+ReportExceptionStackTraces \
    -Dquarkus.native.enable-all-charsets=true

FROM gcr.io/distroless/static-debian12

COPY --from=builder /build/app-server /app/
USER 65532:65532
EXPOSE 8080
ENTRYPOINT ["/app/app-server"]

.NET Native AOT (C# with ASP.NET Core):

// Program.cs — Minimal ASP.NET Core with Native AOT compatibility
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Native AOT: avoid reflection-based DI registrations that require dynamic generation
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddHealthChecks();
builder.Services.AddScoped<IUserService, UserService>();

var app = builder.Build();

// Minimal APIs bypass much of the MVC reflection pipeline — ideal for Native AOT
app.MapGet("/health", () => Results.Ok(new { status = "healthy" }));
app.MapGet("/users/{id:int}", async (int id, IUserService svc) =>
{
    var user = await svc.GetByIdAsync(id);
    return user is null ? Results.NotFound() : Results.Ok(user);
});

// Native AOT requires known reflection points to be registered explicitly
// app.UseStaticFiles(); // ❌ May not work with AOT — use FileResult instead

app.MapHealthChecks("/healthz");
app.Run();

csproj — Native AOT publish configuration:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <!-- Native AOT flag: produces a self-contained static binary -->
    <PublishAot>true</PublishAot>
    <StripSymbols>true</StripSymbols>
    <OptimizationPreference>Speed</OptimizationPreference>
    <!-- Disable features that conflict with AOT -->
    <InvariantGlobalization>true</InvariantGlobalization>
  </PropertyGroup>

  <!-- Native AOT: register reflection-tracked types explicitly -->
  <ItemGroup>
    <NativeLibrary Include="libssl.so" Condition="$([MSBuild]::IsOSPlatform(Linux))" />
  </ItemGroup>

  <ItemGroup>
    <TrimmerRootAssembly Include="MyApp.dll">
      <!-- Prevent trimming of types used via reflection -->
      <ShouldTrim>false</ShouldTrim>
    </TrimmerRootAssembly>
  </ItemGroup>

</Project>

Dockerfile — .NET Native AOT build:

# Stage 1: Compile to native binary
FROM mcr.microsoft.com/dotnet/sdk:9.0-alpine AS build
WORKDIR /src
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -r linux-musl-x64 --self-contained \
    -p:PublishAot=true \
    -p:StripSymbols=true \
    -o /app

# Stage 2: Minimal runtime image
FROM mcr.microsoft.com/dotnet/runtime-deps:9.0-alpine
WORKDIR /app
COPY --from=build /app .
USER appuser
EXPOSE 8080
ENTRYPOINT ["./MyApp"]

Pattern 2: Serverless Cold Start Benchmarking Across Runtimes

This pattern shows how to systematically benchmark cold start times across runtimes in a serverless environment. The benchmark measures the time from initial invocation (or post-idle invocation) to first response, which is the dominant cost for infrequently-called functions.

"""
Serverless runtime cold start benchmark suite.

Measures cold start latency across JVM, .NET, Node.js, and native
runtimes in a container-based serverless environment. Uses identical
hardware profile and network topology for all runs.

Run before committing to a runtime for serverless workloads.
"""

import asyncio
import dataclasses
import json
import statistics
from pathlib import Path
from typing import Any


@dataclasses.dataclass(frozen=True)
class ColdStartResult:
    """Aggregated cold start benchmark result."""
    runtime: str
    version: str
    iterations: int
    mean_ms: float
    median_ms: float
    p95_ms: float
    p99_ms: float
    min_ms: float
    max_ms: float
    std_dev_ms: float
    warm_start_mean_ms: float = 0.0

    def to_dict(self) -> dict[str, Any]:
        return dataclasses.asdict(self)


async def benchmark_runtime(
    runtime_name: str,
    image_name: str,
    invocation_command: list[str],
    iterations: int = 15,
    warm_up_runs: int = 3,
) -> ColdStartResult:
    """Run cold start benchmarks for a single runtime.

    For each iteration: starts a fresh container, sends an HTTP request,
    measures total latency, stops the container. The first N runs are
    considered "warm" to establish baseline steady-state performance.

    Args:
        runtime_name: Human-readable runtime name (e.g., 'Java 21/ZGC').
        image_name: Container image tag to test.
        invocation_command: Docker run command excluding the image name.
        iterations: Number of cold start measurements.
        warm_up_runs: Number of initial warm-up runs excluded from cold stats.

    Returns:
        ColdStartResult with aggregated statistics.
    """
    latencies_ms: list[float] = []
    warm_latencies: list[float] = []

    for i in range(iterations):
        container_id = _run_fresh_container(image_name, invocation_command)
        try:
            latency = _measure_http_latency(container_id)
            if i < warm_up_runs:
                warm_latencies.append(latency)
            else:
                latencies_ms.append(latency)
        finally:
            _stop_container(container_id)

    if not latencies_ms:
        raise ValueError(
            f"No cold start measurements collected for {runtime_name}"
        )

    return ColdStartResult(
        runtime=runtime_name,
        version=_extract_version(image_name),
        iterations=len(latencies_ms),
        mean_ms=statistics.mean(latencies_ms),
        median_ms=statistics.median(latencies_ms),
        p95_ms=percentile(latencies_ms, 95),
        p99_ms=percentile(latencies_ms, 99),
        min_ms=min(latencies_ms),
        max_ms=max(latencies_ms),
        std_dev_ms=statistics.stdev(latencies_ms),
        warm_start_mean_ms=(
            statistics.mean(warm_latencies) if warm_latencies else 0.0
        ),
    )


def _run_fresh_container(image: str, cmd: list[str]) -> str:
    """Start a new container from image and return its ID."""
    import subprocess

    result = subprocess.run(
        ["docker", "run", "-d", "--rm", *cmd, image],
        capture_output=True, text=True, check=False
    )
    if result.returncode != 0:
        raise RuntimeError(f"Failed to start container: {result.stderr}")
    return result.stdout.strip()[:12]


def _measure_http_latency(container_id: str) -> float:
    """Send a single HTTP request and measure round-trip latency in ms."""
    import subprocess
    import time

    # Get host port mapped to container's 8080
    result = subprocess.run(
        ["docker", "port", container_id, "8080"],
        capture_output=True, text=True, check=False
    )
    host_port = result.stdout.strip().split(":")[-1]
    url = f"http://localhost:{host_port}/health"

    start = time.perf_counter_ns()
    subprocess.run(
        ["curl", "-s", "--max-time", "30", url],
        capture_output=True, check=False
    )
    elapsed_ms = (time.perf_counter_ns() - start) / 1_000_000
    return round(elapsed_ms, 2)


def _stop_container(container_id: str) -> None:
    """Stop and remove a container."""
    import subprocess

    subprocess.run(
        ["docker", "stop", "-t", "5", container_id],
        capture_output=True, check=False
    )


def _extract_version(image_name: str) -> str:
    """Extract version string from container image name."""
    parts = image_name.split(":")
    return parts[1] if len(parts) > 1 else "unknown"


def percentile(values: list[float], p: int) -> float:
    """Calculate the p-th percentile of a sorted-free value list."""
    sorted_vals = sorted(values)
    k = (len(sorted_vals) - 1) * (p / 100.0)
    f = int(k)
    c = f + 1 if f + 1 < len(sorted_vals) else f
    d = k - f
    return round(sorted_vals[f] + d * (sorted_vals[c] - sorted_vals[f]), 2)


async def run_full_benchmark_suite() -> list[ColdStartResult]:
    """Execute the complete benchmark suite across all four runtime families.

    Each runtime tests with a minimal HTTP service exposing /health and /users endpoints.
    Results are written to results/cold_start_benchmark.json.
    """
    results = []

    benchmarks = [
        (
            "Java 21/ZGC",
            "myregistry/java21-app:latest",
            ["-p", "8080:8080"],
        ),
        (
            ".NET 8/Server GC",
            "myregistry/dotnet8-app:latest",
            ["-p", "8081:8080"],
        ),
        (
            ".NET 9/Native AOT",
            "myregistry/dotnet9-aot-app:latest",
            ["-p", "8082:8080"],
        ),
        (
            "Node.js 22/V8",
            "myregistry/node22-app:latest",
            ["-p", "8083:8080"],
        ),
        (
            "Go 1.23/static",
            "myregistry/go123-app:latest",
            ["-p", "8084:8080"],
        ),
        (
            "Rust 1.83/native",
            "myregistry/rust183-app:latest",
            ["-p", "8085:8080"],
        ),
    ]

    for name, image, cmd in benchmarks:
        print(f"⏱ Benchmarking {name}...")
        result = await benchmark_runtime(
            runtime_name=name,
            image_name=image,
            invocation_command=cmd,
            iterations=15,
            warm_up_runs=3,
        )
        results.append(result)
        print(f"  Cold start: {result.mean_ms:.0f}ms mean, "
              f"{result.p99_ms:.0f}ms p99")

    # Write results
    output_dir = Path("results")
    output_dir.mkdir(exist_ok=True)
    (output_dir / "cold_start_benchmark.json").write_text(
        json.dumps([r.to_dict() for r in results], indent=2)
    )

    return results

Pattern 3: Container Image Optimization by Runtime

Comparing optimized container images across runtimes for the same application surface area demonstrates why runtime choice directly impacts deployment density and network transfer times.

# === JVM (Java 21) — Optimized with Distroless + Slim JRE ===
FROM eclipse-temurin:21-jre-alpine AS jvm-base

# Install only what the application needs at runtime
RUN apk add --no-cache tini ca-certificates && \
    rm -rf /var/cache/apk/*

# Set JVM flags for container-aware operation
ENV JAVA_TOOL_OPTIONS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseZGC"
ENV TZ=Etc/UTC

FROM gcr.io/distroless/java21-debian12

COPY --from=jvm-base /usr/lib/jvm /usr/lib/jvm
COPY --from=jvm-base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=jvm-base /bin/tini /sbin/tini
COPY target/app.jar /app.jar

USER 65532:65532
ENTRYPOINT ["/sbin/tini", "--", "java", "-jar", "/app.jar"]
# Expected final image size: ~180-250 MB


# === .NET 8 ASP.NET Core — Optimized with Multi-stage + Slim Runtime ===
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine
RUN apk add --no-cache tini && rm -rf /var/cache/apk/*
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["/sbin/tini", "--", "./MyApp"]
USER appuser
# Expected final image size: ~120-180 MB


# === Node.js 22 — Optimized with Alpine + Production-only deps ===
FROM node:22-alpine AS base
RUN corepack enable && corepack prepare pnpm@9 --activate

FROM base AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Tree-shake dev dependencies that slipped through
RUN find node_modules -name "*.d.ts" -exec rm {} \; 2>/dev/null || true
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
# Expected final image size: ~60-100 MB


# === Go 1.23 — Optimized with Multi-stage Static Binary + Scratch ===
FROM golang:1.23-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags="-s -w" -o /app/server

FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /build/app/server /server
ENTRYPOINT ["/server"]
# Expected final image size: ~15-30 MB


# === Rust 1.83 — Optimized with Multi-stage Static Binary + Scratch ===
FROM rust:1.83-alpine AS builder
WORKDIR /build
COPY Cargo.toml Cargo.lock ./
RUN cargo fetch
COPY . .
RUN cargo build --release
RUN strip target/release/myapp

FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /build/target/release/myapp /app
ENTRYPOINT ["/app"]
# Expected final image size: ~5-15 MB

Constraints

MUST DO

  • Benchmark every runtime with the actual application workload — do not rely on published averages alone; architecture and dependency choices can double or halve these figures
  • Use identical container base OS (e.g., Debian bookworm or Alpine 3.20) when comparing image sizes across runtimes to ensure fair comparison
  • Account for JIT warm-up time in autoscaling decisions — JVM and .NET pods that scale up must serve traffic before the JIT compiler optimizes hot paths, causing elevated p99 during burst events
  • Test Native Image / AOT compilation readiness early — reflection-heavy frameworks (Spring Data JPA, Entity Framework with dynamic proxies) may require significant rewrite for native compilation to succeed
  • Measure memory under sustained load, not just at startup — a runtime with low cold-start memory can grow significantly due to JIT metadata cache, GC heap growth, or connection pool accumulation
  • Run the full benchmark suite (Pattern 2) when making a serverless deployment decision — published numbers vary too much by infrastructure provider and invocation pattern

MUST NOT DO

  • Select JVM solely because "it is familiar" without quantifying whether JIT warm-up penalty exceeds cold start budget for the target deployment model
  • Compare a fully-optimized JVM container (distroless, slim JRE) against a default .NET or Node.js image with debug symbols and dev tools included — unfair comparisons produce wrong decisions
  • Assume GraalVM Native Image readiness guarantees production performance — build times can exceed 30 minutes and runtime behavior differs from JIT in edge cases (file descriptor limits, thread naming, TLS negotiation)
  • Choose Node.js for CPU-intensive workloads without benchmarking — the single-threaded event loop will saturate on one core while other cores sit idle, wasting compute budget
  • Deploy native-compilation images to Kubernetes with latest tags — always pin exact image digests (@sha256:) because Native Image and AOT builds are sensitive to OS library versions
  • Skip operational tooling assessment — a runtime with excellent benchmark scores is useless if no profiler, tracer, or debugger exists for it in the target infrastructure

Output Template

When applying this skill to evaluate runtime ecosystems, produce:

  1. Requirements Summary — Restate each quantified deployment requirement with its threshold and source (e.g., "Cold start < 500ms — SRE team SLA, documented in RUN-2024-087").

  2. Runtime Scorecard — A table scoring each runtime against every requirement using a 1–5 scale (5 = exceeds threshold comfortably, 3 = meets with margin, 1 = fails or requires significant optimization effort). Include the numeric benchmark data for each score.

  3. Container Image Comparison — Show final image sizes for each runtime built with the same optimization strategy, measured from the same CI pipeline step. Include layer breakdown where relevant (base OS + runtime + application + dependencies).

  4. Cold Start Benchmark Report — Present the cold start results from Pattern 2: mean, p95, p99, min, max across all iterations for each runtime. Flag any outlier measurements (> 2 standard deviations from mean) with likely cause.

  5. Operational Readiness Assessment — For each remaining candidate, summarize profiling tooling availability, security patch cadence, team competency gaps, and documentation maturity.

  6. Recommendation with Rollback Plan — State the selected runtime, justify against scorecard data, identify the closest runner-up, quantify the gap in key dimensions, and define explicit rollback triggers (e.g., "If p95 cold start exceeds 800ms for 3 consecutive days, fail over to [runner-up]").


Related Skills

SkillPurpose
modern-language-comparisonEvaluates individual languages using microbenchmarks and developer experience metrics — use when runtime decision depends on language-level trade-offs (safety vs performance, ecosystem maturity)
polyglot-developmentDesigns cross-language integration, monorepo build systems, and inter-process communication between services running on different runtimes
framework-selectionScores competing frameworks within a chosen runtime (e.g., Spring Boot vs Quarkus, ASP.NET Core minimal APIs vs MVC) using weighted criteria matrices
version-migrationManages major version upgrades within a runtime stack (e.g., Java 17 → Java 21, .NET 6 → .NET 8) with automated refactoring and zero-downtime rollback strategies

Live References

Authoritative documentation links for runtime ecosystem selection. The model follows markdown links at load time to resolve external references and inline content.


Benchmark Results Reference (Typical Values)

The following table summarizes observed cold start and footprint measurements from representative HTTP services measured on equivalent cloud VMs (2 vCPU, 4 GB RAM). These are empirical baselines — always benchmark your own workload.

RuntimeEnvironmentCold Start (ms)p95 StartupWarm p95 LatencyImage Size (MB)Peak Memory at 1K RPS (MB)
Java 21 / ZGCDocker, distroless2,400 – 6,8005,2008 – 15 ms180–250320–512
.NET 8 / Server GCDocker, Alpine slim1,200 – 3,5002,4004 – 12 ms120–180180–320
.NET 9 / Native AOTDocker, scratch30 – 80603 – 8 ms30–5040–80
Node.js 22 / V8Docker, Alpine150 – 4502802 – 8 ms60–10080–200
Go 1.23 / staticDocker, scratch10 – 40221 – 4 ms15–3020–60
Rust 1.83 / staticDocker, scratch5 – 25141 – 3 ms5–1510–30
Skills guardadas