Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 153 additions & 2 deletions hyperfleet/standards/generated-code-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ Each repository should add appropriate patterns to `.gitignore`. Note the distin

**hyperfleet-api:**
```gitignore
# Generated OpenAPI code
# Generated OpenAPI code (from oapi-codegen)
/pkg/api/openapi/
/data/generated/

Expand Down Expand Up @@ -188,10 +188,161 @@ if git diff --name-only | grep -E "(model_.*\.go|\.pb\.go|_gen\.go)"; then
exit 1
fi
```
---

## 7. Code generator tool

We selected [oapi-codegen](https://github.com/oapi-codegen/oapi-codegen) tool as the generator for our apps.
Here is a detailed comparison among different alternatives:

### OpenAPI Code Generation Comparison

Overview

| Aspect | main/ (OpenAPI Generator) | ogen/ | oapi-codegen/ |
Copy link
Contributor

@rafabene rafabene Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming "main/" to refer to OpenAPI Generator is confusing throughout this section. It appears to be a directory reference from the original analysis rather than the current branch name.

Consider replacing "main/" with "OpenAPI Generator" (or "openapi-gen" if a shorter form is needed) in the tables and headings for clarity

|-----------------|---------------------------|-------------------------------------------|----------------------|
| Files Generated | 34 | 20 | 2 |
| Lines of Code | ~11,274 | ~20,261 | ~2,530 |
| Runtime Deps | None (stdlib only) | ogen-go/ogen, go-faster/jx, OpenTelemetry | oapi-codegen/runtime |

---
#### 1. main/ - OpenAPI Generator (Java-based)

Type Style:
```
type Cluster struct {
CreatedTime time.Time `json:"created_time"`
Name string `json:"name" validate:"regexp=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"`
Spec map[string]interface{} `json:"spec"`
Labels *map[string]string `json:"labels,omitempty"` // pointer for optional
Id *string `json:"id,omitempty"`
}
```

Strengths:
- ✅ No runtime dependencies - uses only stdlib (encoding/json)
- ✅ Null-safety pattern with NullableCluster wrapper types
- ✅ Constructor functions (NewCluster, NewClusterWithDefaults)
- ✅ Validation in UnmarshalJSON - checks required properties
- ✅ GetXxxOk() methods return tuple (value, bool) for presence checking
- ✅ HasXxx() methods for optional field presence
- ✅ ToMap() method for generic map conversion
- ✅ Mature tooling - widely used, extensive documentation

Weaknesses:
- ❌ Verbose - each model in separate file with many boilerplate methods
- ❌ Pointer-based optionals (*string) - less idiomatic for Go
- ❌ No built-in validation beyond required field checking
- ❌ Flattens allOf schemas - loses composition structure
- ❌ Java dependency - requires JVM to run generator

---
#### 2. ogen/ (Go-native generator)

Type Style:
```
type Cluster struct {
ID OptString `json:"id"` // Optional type wrapper
Kind string `json:"kind"`
Labels OptClusterLabels `json:"labels"`
Name string `json:"name"`
Spec ClusterSpec `json:"spec"`
Generation int32 `json:"generation"`
Status ClusterStatus `json:"status"`
}

type OptString struct {
Value string
Set bool
}
```

Strengths:
- ✅ Opt[T] types for optionals - explicit presence tracking, no nil pointer issues
- ✅ Built-in validation (oas_validators_gen.go) with structured errors
- ✅ OpenTelemetry integration - tracing/metrics out of the box
- ✅ Enum validation with MarshalText/UnmarshalText
- ✅ High-performance JSON using go-faster/jx (no reflection)
- ✅ Generated getters/setters for all fields
- ✅ Pure Go toolchain - no JVM needed
- ✅ Server + Client generation in same package
- ✅ Type-safe response types (GetClusterByIdRes interface)

Weaknesses:
- ❌ Largest output (~20k lines) - more code to maintain
- ❌ Heavy runtime dependencies - ogen-go/ogen, go-faster/*, OTel
- ❌ Learning curve - Opt[T] pattern different from idiomatic Go
- ❌ Less flexibility - opinionated about patterns
- ❌ Flattens allOf - doesn't preserve schema composition

---
#### 3. oapi-codegen/ (Go-native generator)

Type Style:
```
type Cluster struct {
// Preserves allOf composition!
ClusterBase `yaml:",inline"`

CreatedBy openapi_types.Email `json:"created_by"` // Typed email
CreatedTime time.Time `json:"created_time"`
Generation int32 `json:"generation"`
Status ClusterStatus `json:"status"`
}

type ClusterBase struct {
APIResource `yaml:",inline"`
Kind string `json:"kind"`
Name string `json:"name"`
Spec ClusterSpec `json:"spec"`
}
```

Strengths:
- ✅ Most compact (~2.5k lines, 2 files) - minimal footprint
- ✅ Preserves allOf composition - embedded structs match schema
- ✅ Semantic types - openapi_types.Email instead of string
- ✅ Lightweight runtime - just oapi-codegen/runtime
- ✅ Pure Go toolchain - no JVM
- ✅ ClientWithResponses - parsed response bodies with type safety
- ✅ RequestEditorFn pattern - clean auth/middleware injection
- ✅ Go-idiomatic - feels like handwritten Go code

Weaknesses:
- ❌ No built-in validation - must add manually or use external
- ❌ Pointer-based optionals (*string) - though less pervasive
- ❌ Fewer accessor methods - direct field access preferred
- ❌ Less observability - no OTel integration
- ❌ Returns *http.Response - need ClientWithResponses for parsed bodies

---
Comparison Summary

| Feature | main/ | ogen/ | oapi-codegen/ |
|--------------------|-------------|--------------|---------------|
| Code Size | Medium | Large | Small ✅ |
| Runtime Deps | None ✅ | Heavy | Light |
| Optional Handling | Pointers | Opt[T] ✅ | Pointers |
| Validation | Basic | Full ✅ | None |
| Schema Composition | Flattened | Flattened | Preserved ✅ |
| Observability | None | OTel ✅ | None |
| Go Idiomaticity | Medium | Medium | High ✅ |
| Type Safety | Good | Excellent ✅ | Good |
| Maintenance | Java needed | Go | Go |

---
**Recommendation**

For our use case (types + client only):

- oapi-codegen is the best fit if you want minimal, Go-idiomatic code that preserves your schema composition (allOf inheritance). The embedded struct pattern (ClusterBase → Cluster) is clean and matches your OpenAPI design.
- ogen is better if you need built-in validation, observability (OTel), or are building a complete server+client solution. The Opt[T] pattern is cleaner than nil pointers.
- OpenAPI Generator (main/) is worth keeping if you need maximum compatibility or zero runtime dependencies, though the Java requirement and verbose output are downsides.
Comment on lines +212 to +340
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix markdown formatting issues.

The static analysis tool identified several formatting inconsistencies that should be corrected:

  1. Missing language specifiers: Code blocks at lines 212, 243, and 282 should specify go as the language
  2. List indentation: Bullet points in Strengths/Weaknesses sections should start at column 0 (no indentation)
  3. Heading spacing: Lines 240 and 279 have extra spaces after ####
  4. Heading style: Line 334's "Recommendation" should be a proper heading (### or ####)
📝 Proposed formatting fixes

Fix code block language specifiers:

-  Type Style:
-```
+  Type Style:
+```go

Apply this pattern to lines 212, 243, and 282.

Fix heading spacing (lines 240, 279):

-####  2. ogen/ (Go-native generator)
+#### 2. ogen/ (Go-native generator)
-####  3. oapi-codegen/ (Go-native generator)
+#### 3. oapi-codegen/ (Go-native generator)

Fix recommendation heading (line 334):

-  **Recommendation**
+#### Recommendation

Fix list indentation - remove the 2-space indentation from all bullet points in Strengths/Weaknesses sections (lines 223-237, 261-276, 302-316, 338-340):

-  Strengths:
-  - ✅ No runtime dependencies - uses only stdlib (encoding/json)
+  Strengths:
+- ✅ No runtime dependencies - uses only stdlib (encoding/json)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
type Cluster struct {
CreatedTime time.Time `json:"created_time"`
Name string `json:"name" validate:"regexp=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"`
Spec map[string]interface{} `json:"spec"`
Labels *map[string]string `json:"labels,omitempty"` // pointer for optional
Id *string `json:"id,omitempty"`
}
```
Strengths:
- ✅ No runtime dependencies - uses only stdlib (encoding/json)
- ✅ Null-safety pattern with NullableCluster wrapper types
- ✅ Constructor functions (NewCluster, NewClusterWithDefaults)
- ✅ Validation in UnmarshalJSON - checks required properties
- ✅ GetXxxOk() methods return tuple (value, bool) for presence checking
- ✅ HasXxx() methods for optional field presence
- ✅ ToMap() method for generic map conversion
- ✅ Mature tooling - widely used, extensive documentation
Weaknesses:
- ❌ Verbose - each model in separate file with many boilerplate methods
- ❌ Pointer-based optionals (*string) - less idiomatic for Go
- ❌ No built-in validation beyond required field checking
- ❌ Flattens allOf schemas - loses composition structure
- ❌ Java dependency - requires JVM to run generator
---
#### 2. ogen/ (Go-native generator)
Type Style:
```
type Cluster struct {
ID OptString `json:"id"` // Optional type wrapper
Kind string `json:"kind"`
Labels OptClusterLabels `json:"labels"`
Name string `json:"name"`
Spec ClusterSpec `json:"spec"`
Generation int32 `json:"generation"`
Status ClusterStatus `json:"status"`
}
type OptString struct {
Value string
Set bool
}
```
Strengths:
- ✅ Opt[T] types for optionals - explicit presence tracking, no nil pointer issues
- ✅ Built-in validation (oas_validators_gen.go) with structured errors
- ✅ OpenTelemetry integration - tracing/metrics out of the box
- ✅ Enum validation with MarshalText/UnmarshalText
- ✅ High-performance JSON using go-faster/jx (no reflection)
- ✅ Generated getters/setters for all fields
- ✅ Pure Go toolchain - no JVM needed
- ✅ Server + Client generation in same package
- ✅ Type-safe response types (GetClusterByIdRes interface)
Weaknesses:
- ❌ Largest output (~20k lines) - more code to maintain
- ❌ Heavy runtime dependencies - ogen-go/ogen, go-faster/*, OTel
- ❌ Learning curve - Opt[T] pattern different from idiomatic Go
- ❌ Less flexibility - opinionated about patterns
- ❌ Flattens allOf - doesn't preserve schema composition
---
#### 3. oapi-codegen/ (Go-native generator)
Type Style:
```
type Cluster struct {
// Preserves allOf composition!
ClusterBase `yaml:",inline"`
CreatedBy openapi_types.Email `json:"created_by"` // Typed email
CreatedTime time.Time `json:"created_time"`
Generation int32 `json:"generation"`
Status ClusterStatus `json:"status"`
}
type ClusterBase struct {
APIResource `yaml:",inline"`
Kind string `json:"kind"`
Name string `json:"name"`
Spec ClusterSpec `json:"spec"`
}
```
Strengths:
- ✅ Most compact (~2.5k lines, 2 files) - minimal footprint
- ✅ Preserves allOf composition - embedded structs match schema
- ✅ Semantic types - openapi_types.Email instead of string
- ✅ Lightweight runtime - just oapi-codegen/runtime
- ✅ Pure Go toolchain - no JVM
- ✅ ClientWithResponses - parsed response bodies with type safety
- ✅ RequestEditorFn pattern - clean auth/middleware injection
- ✅ Go-idiomatic - feels like handwritten Go code
Weaknesses:
- ❌ No built-in validation - must add manually or use external
- ❌ Pointer-based optionals (*string) - though less pervasive
- ❌ Fewer accessor methods - direct field access preferred
- ❌ Less observability - no OTel integration
- ❌ Returns *http.Response - need ClientWithResponses for parsed bodies
---
Comparison Summary
| Feature | main/ | ogen/ | oapi-codegen/ |
|--------------------|-------------|--------------|---------------|
| Code Size | Medium | Large | Small ✅ |
| Runtime Deps | None ✅ | Heavy | Light |
| Optional Handling | Pointers | Opt[T]| Pointers |
| Validation | Basic | Full ✅ | None |
| Schema Composition | Flattened | Flattened | Preserved ✅ |
| Observability | None | OTel ✅ | None |
| Go Idiomaticity | Medium | Medium | High ✅ |
| Type Safety | Good | Excellent ✅ | Good |
| Maintenance | Java needed | Go | Go |
---
**Recommendation**
For our use case (types + client only):
- oapi-codegen is the best fit if you want minimal, Go-idiomatic code that preserves your schema composition (allOf inheritance). The embedded struct pattern (ClusterBase → Cluster) is clean and matches your OpenAPI design.
- ogen is better if you need built-in validation, observability (OTel), or are building a complete server+client solution. The Opt[T] pattern is cleaner than nil pointers.
- OpenAPI Generator (main/) is worth keeping if you need maximum compatibility or zero runtime dependencies, though the Java requirement and verbose output are downsides.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

212-212: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


223-223: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


224-224: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


225-225: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


226-226: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


227-227: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


228-228: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


229-229: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


230-230: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


233-233: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


234-234: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


235-235: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


236-236: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


237-237: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


240-240: Multiple spaces after hash on atx style heading

(MD019, no-multiple-space-atx)


243-243: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


261-261: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


262-262: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


263-263: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


264-264: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


265-265: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


266-266: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


267-267: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


268-268: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


269-269: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


272-272: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


273-273: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


274-274: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


275-275: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


276-276: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


279-279: Multiple spaces after hash on atx style heading

(MD019, no-multiple-space-atx)


282-282: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


302-302: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


303-303: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


304-304: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


305-305: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


306-306: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


307-307: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


308-308: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


309-309: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


312-312: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


313-313: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


314-314: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


315-315: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


316-316: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


334-334: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


338-338: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


339-339: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


340-340: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🤖 Prompt for AI Agents
In @hyperfleet/standards/generated-code-policy.md around lines 212 - 340, The
markdown has formatting issues: add language specifiers "go" to the three fenced
code blocks that follow each "Type Style:" example, remove extra spaces after
the "####" tokens in the generator section headings (e.g., the lines that render
"####  2. ogen/..." and the similar one later), convert the "**Recommendation**"
line into a proper heading (e.g., "### Recommendation" or "####
Recommendation"), and dedent all bullet points in the Strengths/Weaknesses lists
so they start at column 0 (apply this to the three generator
Strengths/Weaknesses blocks and the final comparison list).



---

## 7. References
## 8. References

- [Makefile Conventions](makefile-conventions.md)
- [HYPERFLEET-303](https://issues.redhat.com/browse/HYPERFLEET-303)
Expand Down