Skip to content

Please Format Your Code Blocks: GitHub Issue Etiquette

You are a maintainer. You have carved out thirty minutes between meetings to work through the open issues on your project. You open the first one. The title is promising. The reporter clearly hit a real bug. And then you see it: a wall of unformatted YAML, raw Terraform, and shell output, all smooshed together into a single paragraph, every newline stripped, every indentation gone, triple-quoted strings collapsed into nothing, angle brackets eaten by the Markdown renderer. You cannot tell where the config ends and the error begins.

You close the tab.

If you are a maintainer, you have lived that moment.

If you are a contributor, please keep reading, because this post is for you, and it will help your issues get more attention.

Why Formatting Matters (A Lot More Than You Think)

GitHub issues are the primary communication channel between contributors and maintainers. They are also where bugs get triaged, features get scoped, and, sometimes, frustrations get aired. The quality of that communication directly affects whether a project thrives or grinds to a halt.

When you paste raw, unformatted code into an issue, several things go wrong at once:

Markdown eats your content. Characters like <, >, *, and backticks have special meaning in Markdown. Pasting --output <file> in raw text causes GitHub's HTML sanitizer to strip the <file> argument entirely, rendering it as --output with the angle brackets gone. A string like ERROR: *all* retries exhausted has the word "all" turned into italic emphasis. A YAML block with --- on its own line becomes a horizontal rule. What you pasted and what the maintainer reads are not the same thing.

Indentation disappears. Languages like YAML, Python, and HCL use indentation to express structure. Without a code block, Markdown collapses whitespace. A carefully indented block becomes a flat, unreadable run-on. The maintainer cannot tell what is nested under what, which makes it impossible to reproduce your environment.

There is no syntax highlighting. A fenced code block with a language hint turns on syntax highlighting. Colors make structure visible. Keywords, strings, and comments become distinguishable at a glance. Without it, a hundred-line Terraform file is a wall of gray.

It signals that you did not try. This is the hardest one to say, but it is true. When an issue is poorly formatted, it signals to the maintainer that the reporter did not invest much effort in the report. Maintainers are often volunteers. They respond to that signal by deprioritizing the issue, consciously or not.

The inverse is also true: a well-formatted issue signals effort, care, and respect for the maintainer's time. It gets triaged faster. It gets fixed faster. It makes you a better community member.

The Basics: Fenced Code Blocks

GitHub Markdown supports fenced code blocks using triple backticks. The opening fence can optionally include a language identifier, which enables syntax highlighting.

The general form is:

```language
your code here
```

That is it. Three backticks, a language name, your content, three closing backticks.

If you are not sure of the language name, text always works and at least preserves formatting and whitespace without any highlighting.

Language-by-Language Examples

Here is a practical tour of the languages most commonly encountered in SRE, platform engineering, and DevOps workflows, with real examples of what good formatting looks like.

YAML

YAML is probably the most common language in infrastructure-as-code issues, and also the one most devastated by unformatted pasting. The indentation is load-bearing. Lose it and you lose the entire meaning of the document.

Use ```yaml for YAML content:

app:
  name: my-app
  version: "1.4.2"
  port: 8080
  database:
    host: db.example.com
    name: my_database

Notice how the nesting is immediately clear. Without formatting, that structure collapses.

JSON

JSON is somewhat more resilient than YAML because it uses braces and brackets instead of indentation, but it still benefits enormously from highlighting and preserved whitespace.

Use ```json for JSON content:

{
  "name": "my-service",
  "version": "1.0.0",
  "enabled": true,
  "timeout": 30
}

Shell

Shell scripts and terminal output are two different things that both deserve proper formatting. For shell commands and scripts, use ```shell:

#!/usr/bin/env bash
set -euo pipefail

NAME="${1:-world}"
echo "Hello, ${NAME}!"

For terminal output (what you see when you run a command), use ```text. It preserves whitespace and line breaks without applying shell syntax highlighting to content that is not a script. Preserving the actual output is critical when reporting errors:

Error: connection refused
  host: db.example.com:5432
  caused by: dial tcp: connect: connection refused

Without formatting, that error often turns into a run-on sentence that a maintainer cannot search for, grep through, or paste into a debugger.

PowerShell

PowerShell is everywhere in Windows-based and hybrid infrastructure environments. The ```powershell identifier gives you proper highlighting for cmdlet names, parameters, variables, and strings:

$vms = Get-VM | Where-Object { $_.PowerState -eq "PoweredOff" }
$vms | Select-Object Name, PowerState | Format-Table -AutoSize

PowerShell without syntax highlighting is particularly painful because cmdlet names, parameters, and pipeline operators all blur together. Highlighting makes the intent immediately obvious.

HCL

HashiCorp Configuration Language is used by Terraform, OpenTofu, Packer, Vault, Consul, and Nomad. Use ```hcl for any of these:

variable "region" {
  type    = string
  default = "us-east-1"
}

resource "aws_s3_bucket" "example" {
  bucket = "my-example-bucket"

  tags = {
    Environment = "dev"
  }
}

Packer templates in HCL format also benefit from this:

source "amazon-ebs" "example" {
  region        = var.region
  instance_type = "t3.micro"
  ami_name      = "my-example-ami"
}

build {
  sources = ["source.amazon-ebs.example"]
}

Python

Python's indentation carries semantic meaning, just like YAML. A misaligned block in a raw paste is not just ugly, it is incorrect. Use ```python:

def greet(name: str = "world") -> str:
    """Return a greeting string."""
    return f"Hello, {name}!"


if __name__ == "__main__":
    print(greet())

TypeScript

TypeScript is common in platform tooling, GitHub Actions custom actions, and any Node.js infrastructure code. Use ```typescript:

interface Greeting {
  name: string;
  message: string;
}

function greet(name: string): Greeting {
  return { name, message: `Hello, ${name}!` };
}

console.log(greet("world"));

JavaScript

For JavaScript without TypeScript types, use ```javascript or ```js:

function greet(name = "world") {
  return `Hello, ${name}!`;
}

console.log(greet());

XML

XML appears in Maven pom.xml files, Spring configurations, Ant build files, and plenty of legacy enterprise tooling. Use ```xml:

<?xml version="1.0" encoding="UTF-8"?>
<project>
  <groupId>com.example</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0.0</version>
</project>

Markdown

Yes, you can format Markdown inside a code block. This is useful when you are reporting an issue about a documentation file, a README, or a template. Use ```markdown or ```md.

To nest a fenced code block inside another fenced code block, use four backticks for the outer fence:

# My Project

## Installation

```shell
go install github.com/example/my-project@latest
```

Dockerfile

Dockerfiles are plain text but the ```dockerfile identifier gives you syntax highlighting for instructions like FROM, RUN, COPY, ENV, and ENTRYPOINT:

FROM golang:1.24-alpine

WORKDIR /app

COPY . .
RUN go build -o /app/bin/server ./cmd/server

EXPOSE 8080

ENTRYPOINT ["/app/bin/server"]

Go

For Go source code, use ```go:

package main

import "fmt"

func main() {
    fmt.Println("Hello, world!")
}

Diff

When you want to show what changed between two versions, use ```diff. Lines starting with + are additions, lines starting with - are removals:

--- a/config/app.yaml
+++ b/config/app.yaml
@@ -1,4 +1,4 @@
 app:
   name: my-app
-  version: "1.3.9"
+  version: "1.4.0"

INI and TOML

Configuration files in INI or TOML format appear in many tools. Use ```ini or ```toml respectively:

[app]
name = "my-app"
version = "1.0.0"
debug = false

[database]
host = "db.example.com"
port = 5432

Collapsible Sections with <details>

GitHub Markdown supports a subset of HTML, and one of the most useful elements is <details>. It creates a collapsible section that the reader can expand on demand.

This is incredibly valuable for long command output, stack traces, or full configuration files. Instead of flooding the issue with five hundred lines that the maintainer has to scroll past to find the next piece of context, you wrap it in a <details> block:

<details>
<summary>Full terraform plan output</summary>

```text
Terraform will perform the following actions:

  # aws_s3_bucket.example will be created
  + resource "aws_s3_bucket" "example" {
      + bucket = "my-example-bucket"
      + id     = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.
```

</details>

Which renders as a collapsed section the maintainer can open if they need it:

Full terraform plan output
Terraform will perform the following actions:

  # aws_s3_bucket.example will be created
  + resource "aws_s3_bucket" "example" {
      + bucket = "my-example-bucket"
      + id     = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

The <summary> element sets the label for the collapsed toggle. Use something descriptive like "Full error output", "Complete stack trace", "Raw API response", or "Kubernetes describe output". Avoid generic labels like "click here" because they give no context before expansion.

Blank Lines Are Required

Always leave a blank line after the <summary> closing tag before the opening code fence, and another blank line after the closing code fence before </details>. GitHub's Markdown processor requires those blank lines to recognize the content as Markdown. Without them, your fenced code block renders as plain text with no syntax highlighting.

You can also nest multiple <details> blocks in a single issue, one for the config, one for the error, one for the logs. This turns a wall of text into a structured, navigable report.

When to Use <details>

A good rule of thumb: if the content is more than twenty or thirty lines, consider wrapping it in a <details> block. Classic candidates include:

  • Full stack traces
  • Terraform or OpenTofu plan output
  • kubectl describe output
  • Log file excerpts longer than a screen
  • Full configuration files when only a portion is directly relevant
  • API response bodies
  • Package lock files or dependency trees when relevant
  • Build output from CI

The short version of the relevant section goes in the issue body as a formatted code block. The full version goes in a <details> block right below it.

When to Use a GitHub Gist

Sometimes, even a <details> block is not enough. If your log file is hundreds of lines long, or you need to share multiple interconnected files to provide a minimum reproducible example, you could use a GitHub Gist .

A Gist allows you to host multiple files, retain full syntax highlighting, and keep the main issue description clean. Just create the Gist and drop the link into your issue. This is especially useful for:

  • Massive build logs, core dumps, or extreme stack traces.
  • Providing a complete, runnable reproduction script alongside its required configuration files.
  • Command outputs that exceed GitHub's character limit for issue bodies.

The GitHub Markdown Editor

GitHub's issue editor has two modes: Write and Preview. Use both.

Use the Preview Tab Before Submitting

Switch to the Preview tab before you submit. The Preview tab renders the Markdown exactly as the maintainer will see it. If your code block looks wrong in Preview, fix it before submitting. That thirty-second check eliminates the most common formatting mistakes.

The editor also has a toolbar. The code block button (the one that looks like <>) wraps a single line in inline backticks or wraps a multi-line selection in a fenced code block. Select your text, click the button, and then add the language identifier after the opening backticks on a multi-line block.

For mobile contributors: the GitHub mobile app also supports the Preview tab. There is no excuse not to check.

The Right Language Identifier Matters

Here is a quick reference of the identifiers that GitHub supports for the languages most commonly used in infrastructure and platform engineering:

Language / Tool Identifier
YAML yaml
JSON json
Shell (Bash) shell or bash
PowerShell powershell
HCL (Terraform) hcl
Python python
TypeScript typescript
JavaScript javascript
Go go
Dockerfile dockerfile
XML xml
Markdown markdown
Diff diff
TOML toml
INI ini
Plain text text

When in doubt, text preserves whitespace and indentation without syntax highlighting. It is always better than no code block at all.

GitHub uses Linguist under the hood for syntax detection. The full list of supported identifiers is in the Linguist languages.yml file, but the identifiers in the table above will cover ninety-five percent of what you encounter day to day.

An Issue Template Makes This Automatic

If you are a maintainer reading this and thinking "I wish my contributors did this", you can encode the expectation directly into your issue templates. A bug report template with pre-populated code fences removes the activation energy. The render: is the most direct way to make this automatic:

.github/ISSUE_TEMPLATE/bug.yml
name: Bug
description: Report a bug or unexpected behavior in the library.
labels: ["bug", "triage"]
assignees: ["tenthirtyam"]
projects: ["tenthirtyam/67"]
type: bug
body:
  - type: checkboxes
    id: terms
    attributes:
      label: Code of Conduct
      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/tenthirtyam/example/blob/main/CODE_OF_CONDUCT.md).
      options:
        - label: I agree to follow this project's Code of Conduct.
          required: true
  - type: markdown
    attributes:
      value: |
        Before filing an issue, please [search the existing issues](https://github.com/tenthirtyam/example/issues?q=is%3Aissue+is%3Aopen+label%3Abug) (open or closed), and use the [reaction](https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) feature to add up-votes to existing issues.

        When filing an issue, please include the following information.
  - type: input
    id: version
    attributes:
      label: Library Version
      description: |
        Please provide the library version.
        We recommend testing with [the latest version](https://github.com/tenthirtyam/example/releases/latest).
      options:
        - 1.0.0 (Default)
        - 1.0.1 (Development)
      default: 0
    validations:
      required: true
  - type: input
    id: go-version
    attributes:
      label: Go Version
      description: What version of Go are you using?
      placeholder: e.g. x.y.z
    validations:
      required: true
  - type: dropdown
    id: os
    attributes:
      label: Operating System
      description: Select the operating system where the issue occurs.
      options:
        - Linux
        - macOS
        - Windows
        - Other
    validations:
      required: true
  - type: textarea
    id: description
    attributes:
      label: Description
      description: Please provide a clear and concise description of the issue you are experiencing.
    validations:
      required: true
  - type: textarea
    id: code-sample
    attributes:
      label: Code Sample
      description: |
        - Please provide a minimal, reproducible code sample that demonstrates the issue.
        - Please ensure all sensitive information (passwords, hostnames, etc.) is removed.
      render: go
    validations:
      required: true
  - type: textarea
    id: error-output
    attributes:
      label: Error Output
      description: |
        Please provide the complete error output or stack trace.
        For large outputs, please use a [GitHub Gist](https://gist.github.com/).
      render: text
    validations:
      required: false
  - type: textarea
    id: expected-behavior
    attributes:
      label: Expected Behavior
      description: |
        What is it you expected to happen?
        This should be a description of how the functionality you tried to use is supposed to work.
    validations:
      required: true
  - type: textarea
    id: actual-behavior
    attributes:
      label: Actual Behavior
      description: What actually happened that's different from the expected behavior?
    validations:
      required: true
  - type: textarea
    id: steps-to-reproduce
    attributes:
      label: Steps to Reproduce
      description: Please provide the steps to reproduce the issue.
    validations:
      required: true
  - type: upload
    id: screenshots
    attributes:
      label: Upload Screenshots
      description: Please upload any relevant screenshots or images that can help illustrate the issue.
    validations:
      required: false
  - type: textarea
    id: references
    attributes:
      label: References
      description: |
        Please provide any related GitHub issues or pull requests (open or closed) or documentation.
        Learn about [Referencing Github Issues and Pull Requests](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#referencing-issues-and-pull-requests).
      placeholder: |
        #GH-0000
    validations:
      required: false

The render: field on a textarea in a GitHub issue form template wraps the user's input in a fenced code block automatically. The value can be any language identifier that Linguist supports (the same identifiers from the reference table above). The user does not have to know anything about code formatting: the template handles it for them. This is the most reliable way to ensure well-formatted issues.

The GitHub documentation on issue forms has the full schema.

A Practical Checklist

Before you submit an issue, run through this list:

  • All code, configuration, and commands are in fenced code blocks with a language identifier.
  • All terminal output and error messages are in fenced code blocks (use ```text if unsure).
  • Long outputs (stack traces, plan output, full logs) are wrapped in <details> blocks.
  • The Preview tab shows the issue rendering correctly with no raw characters or collapsed whitespace.
  • The <summary> labels on <details> blocks describe the content clearly.
  • You have removed any sensitive values (tokens, passwords, internal hostnames) before pasting.

That last point deserves emphasis: always review your pasted content for secrets. A terraform.tfvars file or a kubectl describe output can contain credentials, internal IP addresses, or API tokens. Strip those before posting.

Being a Good Community Member

Good formatting is not just a technical practice. It is an act of respect for the people who will read your issue. Maintainers are often volunteers who maintain projects on top of full-time jobs. Every minute they spend decoding a wall of unformatted text is a minute they are not spending fixing bugs.

When you write a well-formatted issue, you are saying:

Quote

"I value your time. I have done the work to make this easy to read and reproduce."

That changes the dynamic of the interaction. It opens a conversation rather than creating friction.

The projects you depend on stay alive because maintainers keep showing up. Help them help you.

Format your code blocks with syntax highlighting.

Resources