ResearchPublished 2026-04-08

Why Does Go Reject the Ternary Operator, and Why Might That Be a Mistake

JFYI: I translated the original article (in Russian) into English with the help of AI.

Go was released in 2009, and one of the deliberate design choices was to omit the ternary operator. The team’s official position, as stated in the Go FAQ, has remained unchanged ever since.

The conditional expression in the form condition ? then : else has been present in industrial languages since 1972. Over the next half-century, it migrated to C++, Java, JavaScript, TypeScript, PHP, Swift, C# and others, and in each of these languages, the community has developed established best practices for its use. Go stands apart as the only widely used language where this decision was made explicitly and is defended with reasoned arguments.

Question and hypothesis

How often do developers encounter code patterns where a ternary operator would be the idiomatic one-line solution?

The hypothesis consists of two testable statements:

  1. The absence of a ternary operator results in a significant amount of redundant code in real-world projects.
  2. Its absence leads to fragmentation of the ecosystem, as independent teams implement incompatible helper functions to solve the same problem.

To test the hypothesis, I conducted a static analysis of several public repositories to obtain two key metrics:

TRP (Ternary Replacement Potential) – proportion of functions containing at least one pattern that can be unambiguously replaced by a ternary expression.

DLOC (Deleted Lines Of Code) – a quantitative measure of the reduction in lines of code resulting from pattern replacement

Significance of the research

The issue has been documented in the public Go tracker since 2019. Issue #33171, opened in July 2019, garnered ~80 comments and 35 contributors. However, it was closed in early October 2019 with the following statement: «We agree that in some cases the ?: syntax would be convenient, but overall adding it to the language seems impractical». By May 2021, the thread had already been locked.

But the creation of new proposals continues. Issue #60502 was opened in May 2023 and closed on the same day, marked as «not planned». Issue #67959, opened in June 2024, was closed as a duplicate on the same day with links to #60502, #31659, #36288, #23248 and #33171. Issue #71808, opened in February 2025 and focused on simplifying error handling via the ternary operator, remained open for less than three days.

The official tracker is just the tip of the iceberg. The topic also regularly pops up on r/golang and r/programming, garnering numerous comments in every new thread. The topics are also discussed on personal blogs on Hacker News, Medium, Habr, and others. The discussions have long since gone beyond the scope of the official tracker and have become constant background noise in the Go community, existing alongside official denials.

There’s a very clear pattern here, the same proposal has been submitted time and again over the past six years, and each time it’s been rejected without any substantive analysis of the data. Who are the authors of these proposals? They are developers with practical experience in C++, Java, TypeScript, and PHP, for whom conditional expressions are a familiar tool. This is not a request from people who have failed to grasp the philosophy of Go, but a request from those who have understood it and continue to work with the language, while considering this specific solution to be flawed.

With this research, my goal is to replace the argument «that’s just how it turned out» with «measurable data».

History of conditional expressions

The conditional expression itself, as a computable unit appeared earlier than is commonly believed, namely, in the late 1950s during work on Lisp – when John McCarthy proposed using cond, which was an expression that returned a value. ALGOL 60, in turn, successfully adopted the idea and included if-then-else as an expression on the right-hand side of an assignment. In essence, this laid the semantic foundation that was later successfully carried over into C.

Dennis Ritchie created C in 1972 at Bell Labs, and it was there that ?: took on its familiar form. C successfully mainstreamed the conditional expression by giving it a compact syntax, lazy evaluation (only one branch is evaluated), and compatibility with system programming. C++ inherited ?: unchanged. Soon, it migrated to other languages, such as Java (1995), JavaScript (1995), PHP (1994), and C# (2000). All of this stems from the C tradition.

In Python, conditional expressions were absent until version 2.5 (2006), and the debate raged on for years (does this remind you of anything?). In the end, Guido van Rossum chose the syntax a if cond else b – deliberately inconvenient for nesting. The Rust and Kotlin languages, however, followed the ALGOL path; in them, if is an expression, and therefore a separate ternary syntax is not needed.

PHP, on the other hand, presents a different set of issues. Prior to version 8.0, the ternary operator was left-associative, unlike in C, Java, and all other languages, where it is strictly right-associative. For example, the conditional chain $a ? "x" : $b ? "y" : "z" was not evaluated as a developer with a background in any other language would expect. An analysis of the top 1000 Composer packages conducted by Nikita Popov as part of the 2019 RFC, identified 12 affected locations – 9 of which turned out to be actual bugs. In PHP 7.4, the behavior received a deprecation warning; in PHP 8.0, it became a compile-time error. This case is very telling because the problem was caused by a specific implementation choice, not the operator itself. And it was fixable.

By 2009, when Go was released, conditional expressions already existed in most widely used languages.

The Go team’s position

The team’s stance is recorded in the Go FAQ and has not changed since publication.

The reason ?: is absent from Go is that the language's designers had seen the operation used too often to create impenetrably complex expressions. The if-else form, although longer, is unquestionably clearer. A language needs only one conditional control flow construct.

This response contains three separate arguments that are worth examining individually.

  1. ?: too often results in expressions that are impenetrably complex. This observation holds true for nested ternary expressions of the form a ? b ? c : d : e. But it follows that the problem lies in the nesting, not in the operator itself. A flat condition ? a : b as the right-hand side of an assignment does not create complexity that could not be created otherwise.
  2. The if-else statement is undoubtedly clearer. The word «undoubtedly» is redundant here. Clarity is highly context-dependent. In a five-line block that initializes a variable based on a condition, the one-line version is easier to read precisely because it contains fewer structural elements to keep track of.
  3. A language needs only one control-flow construct. This argument contains a terminological inaccuracy. The ternary ?: is a conditional expression, not a flow control construct. Flow control changes the order of instruction execution. An expression returns a value. Go already has if, for, switch, select, and goto – five flow control constructs. Banning a sixth by citing «a single construct» does not align with the language’s actual state.

In the Research Significance section, you can see a timeline of issue rejections. This confirms that the team’s position has not changed and that every issue closure referenced the FAQ.

Current alternatives

In the current version of Go (1.26 at the time of writing), developers work around this «issue» in various ways. Each approach either solves the problem only partially and introduces its own trade-offs, or gives rise to other issues.

cmp.Or

Added in Go 1.22, cmp.Or returns the first non-zero argument from a sequence:

result := cmp.Or(userInput, defaultValue)

It works only with zero-value semantics and non-nil values. If userInput intentionally contains zero, an empty string, or false, then cmp.Or interprets this as a missing value. For boolean flags and numeric values where zero is a valid value, this behavior does not apply.

Generics

This is the most common approach among developers. In repositories, you’ll find various names for these helpers, such as If(), Ternary(), Or(), and other variations.

func If[T any](cond bool, a, b T) T {
    if cond {
        return a
    }
    return b
}

Both arguments are evaluated before the function is called – this is a problem with eager evaluation. If(condition, expensiveComputation(), cheapDefault()) will always execute expensiveComputation() regardless of the condition. If the function has side effects or can panic, the code will be incorrect. The function also adds a layer of indirection and hidden logic instead of explicit syntax. This is certainly not a full-fledged replacement and in my opinion looks like an anti-pattern. Additionally, you’ll have to carry this helper function from project to project.

map[bool]T

This kind of "hack" is less common, but the fact remains, it exists and appears in the codebase with rare, yet regular frequency.

result := map[bool]string{true: "yes", false: "no"}[conditions]

This code creates a new map on every execution, evaluates both values, and is much harder to read than the alternatives it supposedly simplifies.

Third-party packages

External packages, such as github.com/samber/lo, provide the functions lo.Ternary[T] and lo.TernaryF. Both fully inherit the issues associated with generic helpers. In defense of lo.TernaryF, it could be argued that it helps solve the problem of eager evaluation. But again – is it worth adding an external dependency just for a one-liner?

Each approach covers different subsets of cases, is incompatible with the others, and requires explanation during code review. This is fragmentation – a direct consequence of the language’s design.

Best practices in other languages

So what about other languages and how do they manage to work with the ternary operator? Among these languages, a solid consensus has emerged: flat usage is the norm, while nesting is an anti-pattern.

Airbnb JavaScript Style Guide:

Ternaries should not be nested and generally be single line expressions.

This rule is enforced by the ESLint rule no-nested-ternary. The nesting restriction is automatically enforced on every commit and is no longer merely a recommendation in the documentation.

The Google Java Style Guide mentions ?: only in the context of formatting, specifically regarding spacing and line breaks. There are no restrictions on its use, and the operator is considered safe enough not to require any caveats.

PEP 8 contains no direct recommendations regarding conditional expressions at all. Python solved the nesting problem at the syntax level, since the a if cond else b construct is structurally awkward for chains, and there is no need to prescribe something that is inherently awkward to write.

clang-tidy uses the readability-avoid-nested-conditional-operator rule. Since nested conditional operators can reduce code readability, they should be split into several separate statements. However, this rule does not apply to flat usage.

MISRA C is a standard for safety-critical systems; it does not prohibit the ternary operator, but merely imposes requirements on the types of operands. The operator is considered sufficiently safe for embedded systems provided that type discipline is observed.

As you can see, the communities didn’t abandon the operator because of the risk of abuse. They restricted a specific anti-pattern – nesting. They kept the operator for simple cases. This is exactly what’s missing in Go – not the operator itself or the rules governing it, but the very ability to make that choice.

Academic context

Structure affects a developer’s cognitive load. And today, this isn’t just intuition – it’s a whole field of measurable research!

There is a metric called cyclomatic complexity, developed by Thomas McCabe (1976). It measures the number of linearly independent execution paths – every if, for, and case increments the counter. This metric is useful for assessing test coverage, but it does not accurately reflect readability. For example, two blocks with the same cyclomatic complexity can differ drastically in perceived complexity.

SonarSource has developed a metric for cognitive complexity. It applies a penalty for each level of indentation. The research paper An Empirical Validation of Cognitive Complexity as a Measure of Source Code Understandability, which analyzed ~24,000 understandability ratings across 427 code snippets, confirms a positive correlation between cognitive complexity and comprehension time as well as subjective readability ratings. Journal of Systems and Software found that these indicators correlate with readability at a level comparable to traditional metrics.

The research paper An Empirical Study of the Relationships between Code Readability and Software Complexity, which analyzed the relationship between readability and complexity in Java code, empirically confirmed a negative correlation and identified nested if-else statements as one of the constructs that most negatively affect readability.

A practical conclusion can be drawn from this: nesting is more costly than writing a condition as a single line. A five-line if-else block for variable initialization adds a level of nesting and requires keeping track of both the declaration context and the value at the same time. A single-line expression eliminates this level of nesting for simple logic.

Go’s argument that if-else is «undoubtedly clearer» seems more like an axiom accepted without verification. And it does not stand up to scrutiny by academic research.

Methodology

Well, data is what the Go team is looking for, judging by the latest issues in their issue tracker. Let’s try to obtain it so that we can use it to support our arguments in the future.

The study uses a mixed-methods approach consisting of the following components:

  • Quantitative part
    static analysis of several public Go repositories, which answers the question of pattern frequency
  • Qualitative part
    thematic analysis of issues and community survey data, which provides context on how developers perceive the problem and what they do to work around it

Both components reinforce each other. Static analysis reveals the scale but not the intent, while a survey reveals the intent but not the scale. Together, they form an argument that is harder to dismiss as subjective.

The following public repositories were selected for analysis:

  1. github.com/golang/go
  2. github.com/hashicorp/terraform
  3. github.com/kubernetes/kubernetes
  4. github.com/traefik/traefik

Automatically generated files (proto, generators, mock, etc.) are excluded, as they skew the statistics upward.

The main tool is a custom parser based on go/token, go/ast, and go/parser. It parses the AST using the standard library, maps it to the taxonomy, filters out generated code, and generates a table of metrics and findings with context. For cross-validation, semgrep is used with independently written rules.

A custom analyzer, not a verifier. It does not analyze side effects, and the figures are considered lower bounds.

The analyzer is available in a public repository: github.com/dmitryburov/go-ternary-audit

Pattern taxonomy

I have identified the four main categories of patterns. A pattern must be unambiguously replaceable with a ternary expression without changing the semantics and without causing issues with eager evaluation.

Category 1. Conditional Assignment

A variable takes on one of two values depending on a condition. This covers both declarations using var followed by an assignment and cases where the variable has already been declared earlier.

var label string
if isAdmin {
    label = "admin"
} else {
    label = "user"
}

// or default + override
label := "user"
if isAdmin {
    label = "admin"
}

// with a ternary operator
label := isAdmin ? "admin" : "user"

The intermediate variable exists here solely as a syntactic artifact and would not be necessary if the if-else statement could return a value.

For example, in the Go sources alone, in the build handler xinit(), you can count around a dozen consecutive patterns.

Category 2. Conditional return

The function returns one of two values depending on the condition. In the current version of Go, this always takes at least three or four lines of code.

if err != nil {
    return http.StatusInternalServerError
}
return http.StatusOK

// with a ternary operator
return err != nil ? http.StatusInternalServerError : http.StatusOK

This pattern is particularly problematic in functions with multiple return points, as each one adds a block, and the function quickly turns into a sequence of similar if-return structures, making it difficult to discern any semantic difference between them.

Category 3. "Bloated" constructor

Calling a function with multiple fields whose values depend on conditions. Each field requires a separate if-else block before the constructor, which breaks the visual connection between the structure and its values.

var label string
if item.Active {
    label = "active"
} else {
    label = "inactive"
}

var priority string
if item.Score > 100 {
    priority = "high"
} else {
    priority = "normal"
}

row := Row{Label: label, Priority: priority}

// With a ternary operator, the constructor reads like a table
row := Row{
    Label:    item.Active      ? "active" : "inactive",
    Priority: item.Score > 100 ? "high"   : "normal",
}

This effect is particularly pronounced in loops, since with three or four fields, the preparatory code takes up several times more space than the constructor itself.

Category 4. Inline expression

In this category, the conditional expression is passed directly as a function argument. This is most commonly seen in fmt.Sprintf, fmt.Printf, log.Printf, and similar functions. An intermediate variable is declared and used exactly once, and exists solely because it is not possible to pass the conditional expression as an argument.

var suffix string
if count != 1 {
    suffix = "s"
}
fmt.Printf("found %d item%s\n", count, suffix)

// With a ternary operator
fmt.Printf("found %d item%s\n", count, count != 1 ? "s" : "")

This pattern category is notable because the disconnect between intent and code is particularly evident here. The developer thinks, «If it’s not singular, add an ‘s’,» but is forced to turn this into a process: declaration -> block -> usage.

So, the four categories cover fundamentally different usage contexts: assignment, return, initialization of composite types, and argument passing. The semantics are the same in all cases – choosing one of two values based on a condition.

Defined metrics

TRP (Ternary Replacement Potential) – the share of functions that contain at least one pattern from the taxonomy, as a percentage of all functions.

TKLOC (Ternary patterns per Kilo Lines Of Code) – the number of patterns per 1,000 lines of code.

DLOC (Deleted Lines Of Code) – the total number of lines saved via pattern replacement.

DLOC delta – the average line reduction per pattern.

Limitations

This study employs a heuristic approach, and it is important to acknowledge its limitations in order to interpret the results.

Generated, mock, test, and other files that could skew the results are excluded from the analysis.

Eager evaluation. Some patterns in categories 3 and 4 contain arguments with side effects or expensive computations. In such cases, replacing them with a ternary operator would be semantically incorrect. The analyzer does not detect side effects, so the actual percentage of correct replacements will be slightly lower.

Sample bias. The selected repositories do not represent the full diversity of Go code. Specifically, they do not include monorepositories of large companies, closed-source corporate code, or infrastructure-generated code. Therefore, the results should be considered indicative.

Detector heuristics. Semantically equivalent code in a non-standard form may be missed. This skews the results downward, and the final figures are a conservative estimate.

Analysis results

golang/go

TRP – 10.58%; every 9th function contains at least one ternary pattern
TKLOC – 4.66; about 4.5 patterns per 1,000 lines of code
DLOC – 2%; about 35,872 lines can be removed out of 1,803,530
DLOC delta – 4.26; about 4 extra lines per pattern

Total functions – 64,863
Total patterns – 8,412

CategoryFoundShareDLOC delta
Conditional return4,91358.4%3.4
Conditional assignment2,64931.5%3.8
Inline expression7438.8%13.7
Bloated constructor1071.3%7.0

hashicorp/terraform

TRP – 11.96%; every 8th function contains at least one ternary pattern
TKLOC – 2.96; about 3 patterns per 1,000 lines of code
DLOC – 1.7%; about 9,961 lines can be removed out of 593,124
DLOC delta – 5.68; about 5.5 extra lines per pattern

Total functions – 11,857
Total patterns – 1,754

CategoryFoundShareLOC delta
Conditional return1,01357.8%4.5
Conditional assignment51129.1%4.9
Inline expression19511.1%12.7
Bloated constructor352.0%11.9

kubernetes

TRP – 14.71%; every 7th function contains at least one ternary pattern
TKLOC – 4.22; about 4 patterns per 1,000 lines of code
DLOC – 2%; about 49,523 lines can be removed out of 2,612,342
DLOC delta – 4.49; about 4.5 extra lines per pattern

Total functions – 60,971
Total patterns – 11,027

CategoryFoundShareDLOC delta
Conditional return7,32466.4%3.9
Conditional assignment2,64023.9%4.2
Inline expression7887.1%9.6
Bloated constructor2752.5%8.6

traefik

TRP – 16.02%; every 6th function contains at least one ternary pattern
TKLOC – 4.22; about 4.2 patterns per 1,000 lines of code
DLOC – 2.15%; about 4,561 lines can be removed out of 195,332
DLOC delta – 5.02; about 5 extra lines per pattern

Total functions – 3,902
Total patterns – 826

CategoryFoundShareDLOC delta
Conditional return50561.1%4.7
Conditional assignment20024.2%3.6
Bloated constructor617.4%6.4
Inline expression607.3%11.2

Discussion

Using static analysis, we identified over 22,000 patterns across more than 140,000 functions. The TRP ranges from 12% to 15% on average, with ternary replacements possible in about 7% of functions. These are specific locations where a developer created a temporary variable, wrote a block of four to five lines, and continued writing code. When multiplied by the size of a typical codebase, these locations will generate constant background noise.

TKLOC consistently stays in the range of 3–5 lines. With an average DLOC delta of ~4.5 lines, this amounts to 1,300 to 2,200 lines of code that exist simply because there is no shorter syntax.

Category 2 (conditional return) turned out to be the most frequent, consistently accounting for ~60%. It is followed by Category 1 (conditional assignment) with ~30%. Three different commands, four different projects – a coincidence?

Analysis of the team’s arguments

Now that we have measurable data and a broader context, let’s try to compare them with the Go team’s arguments.

It creates impenetrably complex expressions

That’s true, but only in one specific case, when there’s a nested structure. a ? b ? c : d : e is indeed hard to read, and the problem is that a conclusion about the operator as a whole has been drawn from a single anti-pattern (which the community clearly identifies).

The data also does not support a generalization. All the patterns found in the sample are flat, single-line assignments and returns without any nesting. That is, these are precisely the cases where the ternary operator is a straightforward and safe replacement.

The && and || operators in complex and cumbersome conditions also generate unreadable code. But for some reason, no one draws the conclusion that they should be removed from the language. And there are plenty of other ways in Go to write unreadable code.

if-else is unquestionably clearer

As I mentioned earlier, the word "undoubtedly" doesn’t hold up to scrutiny. Among the many conditional return patterns found in repositories, the most common is code like if err != nil { return X } return Y, which could be written as a single line. Another common pattern for conditional assignment is var x; if { x = a } else { x = b } instead of x := cond ? a : b.

In both cases, the five-line block requires the reader to keep the declaration, the variable name, the condition, and both values in mind simultaneously. A single-line notation reduces the cognitive load precisely because the information is all in one place. And academic research confirms this.

The team’s argument is not based on any of these studies – it remains an axiom that has not been revisited in the language’s 16-year history.

The language needs only one control flow construct

I would like to point out a terminological error here that needs to be explicitly clarified. A ternary operator is a conditional expression, not a control flow construct, and the difference is quite significant.

A control flow statement changes the order in which instructions are executed: if executes a block or not, for repeats it, goto jumps. An expression is evaluated and returns a value. a ? b : c does not alter the flow – it produces a value that is then used in an assignment or passed as an argument.

That said, Go already has five control flow constructs: if, for, switch, select, goto. It seems that referring to «a single construct» does not reflect the reality of the language.

Ecosystem fragmentation

One of Go’s primary goals is code readability, regardless of who writes it or in which project. "Go code is Go code", and the idea is that an unfamiliar repository can be read without having to study local conventions.

In practice, however, the absence of a ternary operator creates exactly the opposite effect. Every team that needs a single-line conditional statement solves this problem on its own. In many repositories, there are at least three incompatible patterns: cmp.Or, func Ternary[T], and if-else. None of them is standard, none works in all cases, and each requires an explanation upon its first appearance in the codebase.

The third-party package github.com/samber/lo contains the functions lo.Ternary and lo.TernaryF, where the latter takes functions instead of values to work around the eager evaluation issue. This is a perfectly workable solution, but it means: to achieve functionality that most languages provide syntactically a Go developer must include an external dependency.

Fragmentation is a direct consequence of the language’s design. It won’t disappear just because the team closes yet another proposal...

Counterarguments

Like any other language that features a ternary operator, it is not without its drawbacks. And in the interest of fairness, I should present a case where the Go team is absolutely right – and present it in full, without sugarcoating.

Moderate nesting already looks like a conditional statement at first glance, and the reviewer is forced to find the boundaries of the outer expression, then parse the inner one; and when skimming the code, it’s easy to miss the parentheses.

role := isAdmin ? (isSuperAdmin ? "superadmin" : "admin") : "user"

It’s a real «nightmare» when you have three or more levels. Even if you used indentation, it wouldn’t help. What’s important is that the community clearly recognizes this as an anti-pattern and, just as with a cumbersome if-else statement, understands that it’s more idiomatic to use a switch case instead.

result := a > b ? a > c ? "a" : c > b ? "c" : "b" : b > c ? "b" : "c"
// JavaScript, a real pattern from frontend code
const className = isLoading
  ? isSkeleton
    ? 'skeleton'
    : 'spinner'
  : hasError
    ? isRetrying
      ? 'error--retrying'
      : 'error'
    : 'content'

But the problem isn't the operator

The key question remains the same: Is the existence of such examples sufficient grounds for banning the operator?

In general, Go has long had tools that generate code that is difficult to read when misused. Yet no one is suggesting that they be removed.

// Method chains – perfectly readable in simple cases
result := strings.TrimSpace(strings.ToLower(input))

// When overused – the same effect as nested ternaries
val := reflect.TypeOf(getConfig().Handlers[getIndex()].Process(ctx).Result()).Name()
// Goroutines and channels – a powerful tool
go func() { ch <- compute() }()

// When overused – nothing prevents this
go func() { go func() { go func() { ch <- f(g(h(x))) }() }() }()

Go does not prohibit deep call chains or nested goroutines. Even though, if overused, each of them can lead to the same lack of readability.

// Nothing prevents misusing a generic helper
func (p Player) getScore() int {
    return Ternary(p.HasWon, 100, Ternary(p.HasBonus, 80, Ternary(p.HasCombo, 70, 0)))
}

The industry has long solved the problem of nested ternary operators without banning the operator itself. Go could prohibit nesting via go vet or the compiler. Then the main argument against the operator would cease to exist, without depriving developers of the flat, single-line cases for which the operator is needed.

Yes, the fear of a > b ? a > c ? "a" : c > b ? "c" : "b" : b > c ? "b" : "c" is justified, but the right solution is to «ban nesting», not to «ban the operator».

Conclusion

A static analysis of four repositories identified over 22,000 patterns that are structurally identical to flat ternary assignments, with an average TRP of ~13% and TKLOC of 3.5. In a typical Go project, a developer encounters these several times per thousand lines – not as a rare exception, but as routine. Each pattern takes up an average of 3–4 more lines than the logic requires.

The fragmentation of the ecosystem is confirmed by empirical evidence. Incompatible workaround patterns are present in each of the repositories analyzed. There is no standard as such, and this is a direct consequence of the language design.

All three arguments from the Go FAQ do not hold up when applied to flat, single-line cases. The argument about readability refers to nesting, not the statement itself. The argument about the clarity of if-else contradicts academic research on cognitive load. The argument about «a single flow-control construct» contains a terminological error. All three remain in the FAQ without being updated, regardless of what happened in the discussions.

Prior to this study, the discussion had been limited to opinions and anecdotes. It was precisely this lack of data that Ian Lance Taylor cited in 2019 as the reason why the proposal should not be considered. This study fills that gap: it introduces measurable metrics with operational definitions and a reproducible methodology, develops a taxonomy of patterns, and publishes the results in open access.

This study provides the first quantitative measure of the "ternary gap" – the gap between what a language can express concisely and what it lacks to do so.

The response «doesn’t seem worth it», given in 2019 without supporting data, was a position. Now that the data is available, the question is whether the team is prepared to apply the same standard of proof to its own decision that it demanded from the community.

Voting

The vote is open and for informational purposes only. Its results reflect the opinions of the participants and do not claim to provide an objective or statistically accurate picture.

This poll is an integral part of the study. Its purpose is not to obtain representative statistics on all Go developers worldwide, but to verify whether the Go team’s perceptions of how developers view specific syntactic patterns align with what the developers themselves say.

The sample is inherently biased, as it will attract those who are already interested in the topic. There are just four simple questions, each taking less than a minute to answer.

Your vote will help paint a broader picture of developers’ perceptions.

Voting completed!
Thank you for your interest and participation in the poll! The voting statistics for each question is already available below.
Question #1
Which version would you consider more readable in real code?
// Option A
var suffix string
if count != 1 {
   suffix = "s"
}
fmt.Printf("found %d item%s\n", count, suffix)
// Option B
suffix := count != 1 ? "s" : ""
fmt.Printf("found %d item%s\n", count, suffix)
Option A (if-else)37%
Option B (ternary)43%
Both are equally readable14%
It depends on context6%
Question #2
Would you want to see code like this in a project you read?
// Nested ternary operator (right-associative)
label := isAdmin ? (isSuperAdmin ? "superadmin" : "admin") : "user"
Yes, it reads fine8%
Acceptable with constraints (linter/style guide)24%
No, better to use if-else65%
No opinion3%
Question #3
Task: assign a value based on a condition. How do you usually solve this?
// Approach 1
logLevel := "warn"
if isDev() {
  logLevel = "debug"
}
// Approach 2
logLevel := getLogLevel(isDev())
...
func getLogLevel(isDev bool) string { ... }
// Approach 3
import "github.com/samber/lo"
...
logLevel := lo.Ternary(isDev(), "debug", "warn")
Approach 1: assign via if-else57%
Approach 2: extract into a separate function31%
Approach 3: use the generic helper lo.Ternary[T]12%
Question #4
How many years have you been writing Go?
Less than a year20%
1–3 years35%
3–5 years26%
More than 5 years19%

Results

The first issue #78865 was created, backed by statistics, research, and feedback from the community. Within three hours, it was closed as a duplicate of #33171 . Surprised? No.

Quote: “...resubmitting a previously rejected proposal automatically results in rejection.” Sounds like “you don't stand a chance, period.”

But Alan Donovan moved the proposal to a new issue thread #78940, where there was a fairly detailed discussion. You could even say that Go has never come this close to a ternary (conditional) operator before.

The result, unfortunately — “Not planned.”

The community is split roughly down the middle, with no clear majority on either side (as confirmed by the results of the public poll here). And I’m sure we’ll see similar requests from the community in the future.

LanguageChangeLanguageChangeReviewLanguageProposalProposalProposal-FinalCommentPeriod

golang/go#78940adonovanclosed Aug 12, 2026 Proposal

LanguageChangeLanguageProposalProposalDuplicate

golang/go#78865dmitryburovclosed Apr 20, 2026 Proposal