Skip to content

OpenAPI Generator Performance — Optimizing Code Generation Speed and Output Quality

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about OpenAPI Generator Performance. We cover key concepts, practical examples, and best practices to help you master this topic.

OpenAPI Generator performance focuses on reducing generation time through incremental builds, parallel generation, batch processing, and optimizing output to minimize code size for large API specifications.

What You'll Learn

  • Measuring and profiling generation time
  • Incremental generation for faster iterations
  • Parallel generation across languages
  • Batch processing multiple API specs
  • Reducing generated code footprint

Why It Matters

Generation time impacts developer workflow. A spec with 500 endpoints generating 10 language SDKs can take 5+ minutes. DodaTech's generator processes 15 internal API specs across 8 languages daily, and optimization reduced generation time from 12 minutes to 90 seconds.

Real-World Use

A platform with 15 Microservices, each with its own OpenAPI spec, generates SDKs daily. Incremental generation detects unchanged specs and skips regeneration. Parallel processing generates all 8 language SDKs simultaneously. A full pipeline that took 12 minutes now completes in 90 seconds.

flowchart TD
    A["15 API Specs"] --> B{"Spec changed?"}
    B -->|"No"| C["Skip generation"]
    B -->|"Yes"| D["Queue for generation"]
    D --> E["Parallel Group 1"]
    D --> F["Parallel Group 2"]
    D --> G["Parallel Group 3"]
    E --> H["Python SDK"]
    E --> I["TypeScript SDK"]
    F --> J["Java SDK"]
    F --> K["Go SDK"]
    G --> L["C# SDK"]
    G --> M["Ruby SDK"]
    H --> N["Total: 90 seconds"]
    I --> N
    J --> N
    K --> N

Code Examples

Example 1: Profiling Generation Time

import time
import subprocess
import json
from pathlib import Path

def profile_generation(spec_file, generators, output_base='/tmp/sdks'):
    """Profile generation time for each generator."""
    results = {}

    for gen in generators:
        output_dir = f"{output_base}/{gen}"
        start = time.time()

        subprocess.run([
            'openapi-generator-cli', 'generate',
            '-i', spec_file,
            '-g', gen,
            '-o', output_dir,
            '--skip-overwrite'
        ], capture_output=True)

        elapsed = time.time() - start
        file_count = len(list(Path(output_dir).rglob('*')))
        total_size = sum(f.stat().st_size for f in Path(output_dir).rglob('*') if f.is_file())

        results[gen] = {
            'time_seconds': round(elapsed, 2),
            'file_count': file_count,
            'total_size_kb': round(total_size / 1024, 1)
        }

    print(json.dumps(results, indent=2))
    return results

# Profile all target generators
results = profile_generation(
    'openapi.yaml',
    ['python', 'typescript-axios', 'java', 'go', 'csharp-netcore']
)
# Output:
# {
#   "python": {"time_seconds": 3.2, "file_count": 45, "total_size_kb": 234.0},
#   "typescript-axios": {"time_seconds": 4.5, "file_count": 38, "total_size_kb": 189.0},
#   ...
# }

Example 2: Incremental Generation

import hashlib
import json
from pathlib import Path
import subprocess

class IncrementalGenerator:
    def __init__(self, cache_file='/tmp/gen_cache.json'):
        self.cache_file = Path(cache_file)
        self.cache = self._load_cache()

    def _load_cache(self):
        if self.cache_file.exists():
            return json.loads(self.cache_file.read_text())
        return {}

    def _save_cache(self):
        self.cache_file.write_text(json.dumps(self.cache, indent=2))

    def _spec_hash(self, spec_file):
        """Compute hash of spec file contents."""
        return hashlib.sha256(Path(spec_file).read_bytes()).hexdigest()

    def needs_generation(self, spec_file, generator):
        """Check if regeneration is needed."""
        current_hash = self._spec_hash(spec_file)
        cache_key = f"{spec_file}:{generator}"
        cached_hash = self.cache.get(cache_key)

        if cached_hash == current_hash:
            print(f"SKIP {generator}: spec unchanged")
            return False

        print(f"GENERATE {generator}: spec changed")
        return True

    def mark_generated(self, spec_file, generator):
        """Mark spec as generated for this generator."""
        cache_key = f"{spec_file}:{generator}"
        self.cache[cache_key] = self._spec_hash(spec_file)
        self._save_cache()

    def generate(self, spec_file, generators, output_base='/tmp/sdks'):
        """Generate only changed specs."""
        for gen in generators:
            if self.needs_generation(spec_file, gen):
                output_dir = f"{output_base}/{gen}"
                subprocess.run([
                    'openapi-generator-cli', 'generate',
                    '-i', spec_file,
                    '-g', gen,
                    '-o', output_dir,
                    '--skip-overwrite'
                ], check=True)
                self.mark_generated(spec_file, gen)

# Usage
gen = IncrementalGenerator()
gen.generate('openapi.yaml', ['python', 'typescript', 'java'])
# First run: generates all three
# Second run (no spec change): skips all three

Example 3: Batch Parallel Generation

import concurrent.futures
import subprocess
import time

def generate_sdk(spec_file, generator, output_base):
    """Generate SDK for a single generator."""
    output_dir = f"{output_base}/{generator}"
    start = time.time()

    result = subprocess.run([
        'openapi-generator-cli', 'generate',
        '-i', spec_file,
        '-g', generator,
        '-o', output_dir
    ], capture_output=True, text=True)

    elapsed = time.time() - start
    return {
        'generator': generator,
        'success': result.returncode == 0,
        'time': round(elapsed, 2),
        'error': result.stderr[:200] if result.returncode != 0 else None
    }

def batch_generate(spec_file, generators, output_base='/tmp/sdks',
                   max_workers=4):
    """Generate SDKs in parallel with worker limit."""
    print(f"Generating {len(generators)} SDKs with {max_workers} workers")

    with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = [
            executor.submit(generate_sdk, spec_file, gen, output_base)
            for gen in generators
        ]

        results = []
        for future in concurrent.futures.as_completed(futures):
            result = future.result()
            status = "PASS" if result['success'] else "FAIL"
            print(f"  [{status}] {result['generator']}: {result['time']}s")
            results.append(result)

    total_time = sum(r['time'] for r in results)
    wall_time = time.time() - start_time

    print(f"\nTotal CPU time: {total_time}s")
    print(f"Wall clock time: {wall_time:.2f}s")
    return results

# Parallel batch generation
start_time = time.time()
results = batch_generate(
    'openapi.yaml',
    ['python', 'typescript-axios', 'java', 'go',
     'csharp-netcore', 'ruby', 'php', 'swift5'],
    max_workers=4
)

Common Mistakes

1. Full Generation on Every Change

Use incremental generation. Most spec changes affect only a few endpoints.

2. Sequential Generation for Multiple Languages

Always parallelize language generation. Languages are independent of each other.

3. Generating Unused Generators

Only generate the languages your teams actually use. Each unused generator wastes time.

4. Ignoring Generator-Specific Options

Some generators are faster with specific flags. Research and benchmark generator options.

5. Not Caching Generator Outputs

Cache generated code across CI runs. Only regenerate when the spec or generator version changes.

Practice Questions

  1. How does incremental generation improve performance?
  2. When should you parallelize generation?
  3. What affects generation time the most?
  4. How do you measure generation performance?
  5. What strategies reduce generated code size?

Answers:

  1. It skips regeneration for specs that haven't changed, saving time in CI/CD pipelines with multiple specs.
  2. When generating SDKs for multiple languages — each language is independent and can run in parallel.
  3. Spec size (number of endpoints and schemas), generator complexity, and output filesystem I/O.
  4. Time each generator, count output files, and measure total output size.
  5. Use --additional-properties=skipDefaultInterface=true, remove unused models, and trim schema descriptions.

Challenge: Build a batch generation pipeline that processes 5 API specs, generates 6 language SDKs each, uses incremental caching and parallel workers. Profile and optimize to complete in under 60 seconds.

FAQ

Why is generation slow for large specs?

: OpenAPI Generator builds an internal code model, applies templates, and writes files. Each operation and schema adds processing time.

Can I use a generation server for faster builds?

: Yes. The OpenAPI Generator CLI can run as a server, keeping the JVM warm and reducing startup overhead.

How much memory does generation require?

: Typically 512MB-2GB depending on spec size and generator. Use --max-memory JVM flags.

Does generation time scale linearly with spec size?

: Roughly O(n) for endpoints, but schema complexity can cause super-linear scaling due to inheritance resolution.

Can I skip model generation?

: Yes. Use --api-only or --model-only flags when you only need part of the code.

What's Next

Optimize your {{< ilink "OpenAPI" "CI/CD Code Generation with OpenAPI Generator" }} pipeline, and learn {{< ilink "OpenAPI" "OpenAPI Generator Customization" }} to reduce unnecessary output.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro