# How the JetBrains Go Feature Generator (featuresgen) Creates FEATURES.md

> Discover how the JetBrains featuresgen tool converts guidelines.json into FEATURES.md by parsing JSON, building Markdown tables, and adding syntax-highlighted code examples. Learn the process.

- Repository: [JetBrains/go-modern-guidelines](https://github.com/jetbrains/go-modern-guidelines)
- Tags: internals
- Published: 2026-09-04

---

**The `featuresgen` tool transforms the machine-readable [`guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/guidelines.json) into the human-readable [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) document by parsing the JSON into Go structs, constructing Markdown tables and anchored sections with `strings.Builder`, and writing syntax-highlighted code examples.**

The `featuresgen` utility in the [JetBrains/go-modern-guidelines](https://github.com/JetBrains/go-modern-guidelines) repository serves as the bridge between structured data and documentation. This feature generator eliminates manual synchronization errors by programmatically converting the JSON schema output into a comprehensive Markdown reference that includes impact ratings, version information, and before/after code comparisons.

## The featuresgen Architecture and Data Flow

Located at [`internal/guidelines/featuresgen/main.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/featuresgen/main.go), the feature generator operates as a single-purpose command-line transformer. It expects exactly two arguments: the source JSON path and the destination Markdown path.

### Input and Output Specifications

The tool processes [`guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/guidelines.json), generated by the `schema` package, which contains a serialized slice of `schema.Guideline` structs. Each struct includes fields such as `ID`, `Category`, `SinceVersion`, `Impact`, `Modernizer`, `Guideline`, `Details`, and `Examples`. The output is a fully formatted [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) file featuring a summary table and detailed per-guideline sections with anchored hyperlinks.

### Core Dependencies

The program relies on three critical components from the codebase:
- **[`internal/guidelines/schema/schema.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/schema/schema.go)**: Defines the `Guideline` struct and the `Parse(data []byte)` function that unmarshals JSON.
- **`strings.Builder`**: Accumulates the Markdown content efficiently without excessive allocations.
- **Standard library**: `os.ReadFile` and `os.WriteFile` handle filesystem operations.

## Step-by-Step Generation Process

The `featuresgen` pipeline executes in five distinct phases, each handled within the `run` and `render` functions.

### CLI Argument Validation

The `run` function strictly validates input before processing. It aborts with a usage error if the argument count differs from two:

```go
if len(args) != 2 { 
    return fmt.Errorf("usage: featuresgen <guidelines.json> <FEATURES.md>") 
}

```

This ensures the generator receives the correct input JSON and output Markdown file paths.

### JSON Parsing with the schema Package

After reading the raw bytes with `os.ReadFile(args[0])`, the program delegates decoding to the schema package:

```go
guidelines, err := schema.Parse(data)

```

The `schema.Parse` function unmarshals the JSON into a `[]schema.Guideline` slice, handling type safety for boolean flags like `Modernizer` and nested example arrays.

### Markdown Construction Pipeline

The `render` function orchestrates Markdown generation using a `strings.Builder` instance. The construction occurs in three sequential stages:

1. **Header and Legends**: Fixed introductory text explains the modernizer notation and impact level meanings.
2. **Summary Table**: A Markdown table links categories to guideline IDs with anchor references:
   ```go
   fmt.Fprintf(&b,
       "| %s | [`%s`](#%s) | %s | %s | %s |\n",
       guideline.Category,
       guideline.ID,
       guideline.ID,
       modernizerMark(*guideline.Modernizer),
       guideline.SinceVersion,
       guideline.Impact,
   )
   ```

3. **Detailed Sections**: The tool iterates the guideline slice a second time to emit full documentation sections. Each section includes:
   - An HTML anchor tag (`<a id="GUIDELINE_ID"></a>`) for intra-document linking
   - Metadata lines showing the modernizer status (via `modernizerLabel`), Go version, and impact
   - Verbatim `Guideline` and `Details` strings
   - Before/After code blocks rendered via `writeCodeBlock`

### File Writing and Persistence

Finally, the accumulated buffer converts to bytes and writes to disk:

```go
err = os.WriteFile(args[1], []byte(b.String()), 0644)

```

This atomic operation ensures [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) updates only when the entire document generates successfully.

## Key Implementation Details

### The render Function Logic

The `render` function maintains purity by accepting `[]byte` and returning `[]byte`, making it fully testable without side effects. It separates concerns between data transformation (parsing) and presentation (Markdown formatting).

### Markdown Table Generation

The `modernizerMark` helper converts the boolean `Modernizer` field into visual indicators:
- `[x]` indicates the guideline has a supported modernizer
- `[ ]` indicates no automated modernization tool exists

### Code Block Formatting

The `writeCodeBlock` utility ensures consistent Go syntax highlighting by wrapping example lines in fenced code blocks:

```go
func writeCodeBlock(b *strings.Builder, lines []string) {
    b.WriteString("```go\n")
    for _, line := range lines {
        b.WriteString(line)
        b.WriteByte('\n')
    }
    b.WriteString("```\n")
}

```

This produces properly formatted Before/After comparisons for every code example in the guidelines.

## Testing and Validation

The repository includes comprehensive unit tests in [`internal/guidelines/featuresgen/main_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/featuresgen/main_test.go) that verify the `render` function output. These tests ensure that:
- Anchor tags generate correctly for every guideline ID
- The Markdown table syntax remains valid
- Special characters in guideline text escape properly

By validating the byte-to-byte transformation, the test suite guarantees that [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) remains synchronized with [`guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/guidelines.json) across repository updates.

## Summary

- **Input**: `featuresgen` reads [`guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/guidelines.json) from [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) via `os.ReadFile`.
- **Parsing**: The `schema.Parse` function in [`internal/guidelines/schema/schema.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/schema/schema.go) decodes JSON into typed structs.
- **Transformation**: The `render` function uses `strings.Builder` to construct Markdown tables, anchored sections, and code blocks.
- **Output**: The final Markdown writes to [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) via `os.WriteFile` with 0644 permissions.
- **Testing**: Unit tests in [`main_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/main_test.go) verify the output format matches expectations without requiring filesystem I/O.

## Frequently Asked Questions

### What input format does featuresgen require?

The tool requires a [`guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/guidelines.json) file produced by the schema package containing a JSON array of guideline objects. Each object must include fields like `ID`, `Category`, `SinceVersion`, `Impact`, `Modernizer`, `Guideline`, `Details`, and `Examples` to parse correctly via `schema.Parse`.

### How does featuresgen handle code examples in the output?

The `writeCodeBlock` function wraps example code lines in Go-syntax fenced code blocks (```go). For each guideline, it renders separate "Before" and "After" blocks to demonstrate the modernization pattern, ensuring the output Markdown renders with proper syntax highlighting in GitHub and JetBrains IDEs.

### Can I run featuresgen without the full JetBrains repository?

Yes, the tool is self-contained within `internal/guidelines/featuresgen`. You can execute it independently using `go run ./internal/guidelines/featuresgen <input.json> <output.md>` provided you have a valid [`guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/guidelines.json) file conforming to the schema defined in [`internal/guidelines/schema/schema.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/schema/schema.go).

### Why does featuresgen iterate the guideline slice twice?

The first iteration builds the summary table at the top of [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md), while the second iteration generates the detailed per-guideline sections below. This two-pass approach ensures the table of contents contains anchor links to sections that appear later in the document, creating the navigation structure visible in the final Markdown.