Skip to content

Programming Idioms

Idiom

Last updated: 2026-05-17 · 18 min read

Programming Idioms

A Programming Idiom is a conventional way to write relatively small code constructs within a specific programming language or programming context. In Korean, it is commonly translated as idiomatic expression(관용구).

An idiom is not part of the language definition itself, but forms within the range of expressions the language permits. This mirrors the structure of linguistic idioms, which exist within the rules of words and grammar yet cannot be derived from those rules alone.1

Code that conforms to idioms is called idiomatic, while code that deviates from them is called idiosyncratic. Idiosyncratic code may be grammatically correct but reads as unnatural to someone familiar with the language.

Code
Paradigm = A way of thinking about programs

Principle = An abstract rule that guides judgment / decision-making

Pattern = A reusable solution to a recurring design problem

Idiom = A small, natural way of expressing something within a given language / context

Syntax = The rules of expression that a language allows

The five levels above represent an abstraction hierarchy from thought to expression. Idioms apply at a smaller unit than patterns and are more abstract than syntax.

Core Definition

A Programming Idiom satisfies the following conditions.

1. It is strongly tied to a specific language, ecosystem, or framework.

2. It applies to relatively small code components (from a single line to a few dozen lines).

3. It is closer to a style of expression than to the problem itself.

4. It creates expectations for code readability and maintenance.

5. It is not syntactically mandatory, but in practice it operates almost as a de facto standard.

In terms of scale, idioms are smaller than Design Patterns. Both are templates to follow rather than code to copy and paste, but Design Patterns typically deal with the interactions of multiple objects or modules, whereas idioms address expression at the level of one to a few dozen lines.[1] For example, the Singleton pattern is a Design Pattern, while releasing resources via a using block in C# is an idiom.

Why It Matters

The value of idioms is most clearly apparent in team settings. When multiple expressions are possible for the same task in a given language, following the idiomatic expression reduces the cognitive load on the reader.[1] It takes less time to read the code, there is no need to infer intent, and unexpected behavior can be ruled out more quickly.

Without knowing the idioms:

Code
- The code is syntactically correct but feels awkward.
- It conflicts with the standard library of the ecosystem.
- Other developers have to infer the intent when reading the code.
- It misses the safety guarantees and optimization paths that the language provides.
- You keep receiving the same feedback in code reviews.

Conversely, when you know the idioms:

Code
- The intent of the code is quickly understood at a glance.
- It does not go against the language's fundamental constraints.
- It fits the expected forms of libraries and tooling.
- It automatically follows conventions that prevent bugs.
- The cost of collaboration is reduced.

Code frequently shared in crowdsourced environments such as StackOverflow and GitHub effectively becomes the medium through which idioms are learned, and this is known to play a key role in helping developers cross language barriers.2

Personal note: In that sense, being Korean is itself a kind of curse as a programmer. The gap between not being a native English speaker and thinking in Korean is significant.

Idioms vs. Design Patterns

Design Patterns address recurring design problems that transcend any particular language, while idioms are closer to the expressive conventions of a specific language. That said, the two form a continuous spectrum, and idioms can reasonably be viewed as small patterns.

Code
Design Pattern:
- An abstract solution structure for recurring design problems
- Operates at the level of interaction among multiple objects / modules
- Language-independent (implementation may vary by language)

Programming Idiom:
- A natural and conventional way of expressing something in a specific language
- Ranges from a single line to a few dozen lines of code
- Language-dependent

For example, the Strategy pattern can be implemented in many languages, but the form of the implementation varies from language to language.

Code
Strategy pattern (idioms by language):

- Java: `interface` + `class`
- Python: callable / duck typing
- JavaScript: function object
- C#: `interface` / `delegate`
- Rust: trait / enum dispatch
- Haskell: passed as a function argument, or using type classes

Even the same pattern must be expressed in a way that fits each language's idioms. Directly transcribing the UML structure from a pattern book may be grammatically correct but can produce code that feels awkward in the target language.

Idioms vs. Paradigms

A Programming Paradigm is a broad way of thinking about programs. Object-oriented programming, functional programming, and procedural programming all fall into this category.

Idioms are smaller in scope. If a paradigm addresses "how to think," an idiom is closer to "how to naturally express that thinking in this particular language."

Summarized as five levels of abstraction, they are as follows.

Code
[Large abstraction]

- Paradigm: A way of thinking (object-oriented, functional, procedural)
- Principle: A criterion for making decisions (SOLID, DRY, KISS, YAGNI)
- Pattern: A solution to a recurring problem (Strategy, Observer, Factory)
- Idiom: A natural expression within a language's community (RAII, list comprehension)
- Syntax: The rules that define valid expressions (if, for, class declarations)

[Small unit]

Language-Specific Examples

Python

Python's idioms are commonly referred to as Pythonic.

Python
# Less Pythonic
result = []
for i in range(len(items)):
    result.append(items[i].name)

# More Pythonic
result = [item.name for item in items]

Representative Python Idioms:

  • list / dict / set comprehension

  • context manager (with)

  • iterator / generator

  • enumerate, zip

  • EAFP (Easier to Ask Forgiveness than Permission): exception-based control flow

  • The contrasting concept, LBYL (Look Before You Leap): pre-check-based control flow. In Python, EAFP is considered more idiomatic.

  • duck typing

  • __init__.py-based module boundaries

Python
with open("data.txt", "r", encoding="utf-8") as file:
    text = file.read()

The code above never calls file close directly. Having the with statement guarantee resource release is the standard idiom in the Python ecosystem.

C++

The defining idiom of C++ is RAII.

C++
{
    std::lock_guard<std::mutex> lock(mutex);
    // critical section
}

Lock acquisition and release are tied to the object's lifetime. This approach guarantees that the lock is released even if an exception is thrown or an early return occurs.

Representative C++ Idioms:

Category

Idiom

Resource Management

RAII, Rule of Zero/Three/Five, Smart pointer ownership, copy-and-swap

Encapsulation

Pimpl, NVI

Type Representation

std::optional, std::variant + std::visit

Compile-Time

CRTP, Type erasure, EBO

STL

Erase-remove

(Language Feature)

range-based for, move semantics

Idioms are especially important in C++. Because the language provides many powerful but dangerous low-level features, idioms effectively serve as safety mechanisms.

For detailed C++ idioms, see here.

Personal note: Personally, I think C++'s biggest problem is that there are too many versions and it is heavily fragmented. I enjoy using programs written in C++, but I'd rather not encounter it in codebases I have to work on.

JavaScript / TypeScript

In JavaScript and TypeScript, asynchronous handling and object composition patterns are important idioms.

TypeScript
// Less idiomatic
fetch(url)
  .then((response) => response.json())
  .then((data) => {
    console.log(data);
  });

// More common form in modern TypeScript
const response = await fetch(url);
const data = await response.json();
console.log(data);

Representative JS/TS Idioms:

  • async / await

  • object destructuring, spread/rest

  • immutable update

  • discriminated union

  • type guard

  • optional chaining (?.)

  • nullish coalescing (??)

  • module boundary-centered design

In TypeScript, rather than simply attaching types, what matters is making types describe the runtime structure.

TypeScript
type Result<T> =
  | { ok: true; value: T }
  | { ok: false; error: string };

function handle(result: Result<number>) {
  if (result.ok) {
    return result.value;
  }

  throw new Error(result.error);
}

This kind of discriminated union is the representative TypeScript idiom for explicitly expressing error paths.

C#

In C#, LINQ, using, async/await, properties, and dependency injection all function as strong idioms.

C#
using var stream = File.OpenRead(path);

Delegating the lifetime of IDisposable resources to using is the C# way.

Representative C# idioms:

  • using / IDisposable

  • LINQ

  • property

  • async/await + Task<T>

  • extension method

  • nullable reference type (string?)

  • dependency injection

  • record type

  • pattern matching (switch expression)

Go

Go's idioms place a strong emphasis on explicitness and simplicity.

Go
result, err := doSomething()
if err != nil {
    return fmt.Errorf("doing something: %w", err)
}

Representative Go idioms:

  • explicit error return + err != nil check

  • error wrapping with fmt.Errorf + %w

  • goroutine + channel-based concurrency

  • resource release scheduling with defer

  • implicit interface satisfaction (in contrast to explicit declaration in Java/C#)

  • preference for small interfaces (io.Reader, io.Writer)

  • simple import paths matching the package name

Go
type Reader interface {
    Read(p []byte) (n int, err error)
}

Keeping interfaces small and letting implementations satisfy them implicitly reflects Go's expressive philosophy.

Rust

Rust's idioms are shaped around the ownership model and explicit error handling.

Rust
fn read_config(path: &Path) -> Result<Config, ConfigError> {
    let contents = fs::read_to_string(path)?;
    let config: Config = toml::from_str(&contents)?;
    Ok(config)
}

Representative Rust idioms:

  • Result<T, E> + ? operator

  • Option<T> + pattern matching

  • ownership transfer (move) vs. borrowing (&, &mut)

  • trait-based abstraction

  • match with exhaustiveness checking

  • builder pattern (StructBuilder::new().with_x().build())

  • newtype wrapper for semantic separation

  • avoiding gratuitous .clone() calls

Rust
match value {
    Some(x) if x > 0 => println!("positive: {}", x),
    Some(_) => println!("non-positive"),
    None => println!("missing"),
}

What reflects Rust's expressive sensibility is that pattern matching is used not merely as branching, but as a direct query against data structure.

Haskell

Haskell's idioms center on function composition and leveraging the type system.

Haskell
processItems :: [RawData] -> [Result]
processItems = map transform . filter isValid

Representative Haskell idioms:

  • function composition (.) and point-free style

  • expressing monadic computation with do-notation

  • ad-hoc polymorphism via type classes

  • Maybe / Either for representing absence and failure

  • pattern matching + guards

  • infinite data structures leveraging lazy evaluation

  • Functor / Applicative / Monad abstractions

Haskell
readConfig :: FilePath -> IO (Either String Config)
readConfig path = do
    contents <- readFile path
    return (parseConfig contents)

In Haskell, idioms are often directly tied to mathematical abstractions such as monads and functors, making this the area where the most is lost in translation to other languages.

Anti-idioms: Expressions to Explicitly Avoid

Negative examples are often more effective than positive ones when teaching idioms. Each language ecosystem has a catalog of anti-idioms that are explicitly considered "do not write it this way."

Python:

- Mutable default argument: def f(x=[]):

- Bare except clause: except: (without specifying exception type)

- Modifying global variables (using global keyword)

- Public API without type hints

C++:

- using namespace std; in headers

- Raw new / delete (instead of smart pointers)

- C-style casts (e.g., (int)x)

- Polymorphic class without virtual destructor

JavaScript / TypeScript:

- == (loose comparison; use === instead)

- var (use let / const)

- Bypassing the type system with any (TypeScript)

- Mutating React state directly

C#:

- async void (except for event handlers)

- .Result / .Wait() (synchronous blocking)

- Empty catch block

- Using IDisposable without using

Go:

- Ignoring errors _ = err)

- Assuming nil channels / slices will behave as expected

- Goroutine leaks (infinite wait without context)

- Using panic for normal error flow

Rust:

- Indiscriminate .clone() to bypass the borrow checker

- Overusing unwrap() (instead of handling Result / Option directly)

- Overusing unsafe blocks

- Using large enum variants without Box

Anti-idioms are not merely style issues. Most have real impact on safety or performance, and they are actively detected by linters and static analysis tools.

The Temporal Nature of Idioms

Idioms are not fixed constants. They change across generations as language standards are revised, standard libraries evolve, and community consensus shifts. Therefore, when discussing idioms, a temporal coordinate of "which era's idioms" is needed alongside the discussion.

Generational Changes in C++

Code
C++98 era:
- `std::auto_ptr`
- Raw `new` / `delete`
- Manually written container iteration

Post-C++11:
- `std::unique_ptr` / `std::shared_ptr`
- Range-based `for`
- `auto`, lambdas, move semantics

Post-C++17 / C++20:
- `std::optional`, `std::variant`
- Structured bindings
- Concepts, modules, coroutines

C++98 code still works if left as-is, but from a modern C++ perspective, it is no longer idiomatic.

Generational Changes in Python

Code
Python 2:
- `print` statement, explicit unicode (`u"..."`)
- `xrange` / `range` distinction
- Old-style classes vs. new-style classes

Python 3:
- `print` as a function, strings are unicode by default
- f-strings (3.6+)
- Type hints (3.5+, union with `|` in 3.10+)
- `dataclass`, `asyncio`
- Destructuring and `match`-`case` (3.10+)

Generational Changes in JavaScript

Code
ES5 era:
- `var`, function declarations
- Prototype-based inheritance
- Callback hell

Post-ES6 (ES2015+):
- `let` / `const`, arrow functions
- `class` keyword, module `import`/`export`
- `Promise`, `async`/`await`
- Destructuring, spread / rest operators
- Optional chaining, nullish coalescing (ES2020)

Even within the same language, when writing or reviewing code, one should be conscious of "which generation's idioms this belongs to." When maintaining an older codebase, consistently preserving the idioms of that era may be preferable to mixing in newer-generation idioms.

Sources and Authority of Idioms

Who decides idioms? There is no single authority; they emerge from the interaction of multiple sources.

Code
1. Language designers / official proposals
- Python's PEPs, Rust's RFCs, JS's TC39 proposals
- Official recommendations are the starting point for idioms

2. How the standard library uses itself
- How the standard library uses its own APIs becomes the de facto standard
- Python's `itertools`, Go's `io` package, Rust's `std` modules

3. Default rules of linters / formatters
- `rustfmt`, `gofmt`, `prettier`, `eslint`, `ruff`, `pylint`
- Automatically enforced, thus the strongest practical force

4. De facto standards from popular open source
- React codebase, Django projects, Kubernetes source
- Conventions from large projects spread throughout the community

5. Authoritative books and guides
- Effective C++, Effective Java, Fluent Python
- The Rust Book, Real World Haskell
- Google Style Guide

When these sources conflict (for example, when the style recommended by rustfmt differs from community practice), deciding which to follow is a matter of practical judgment. Generally, (3) linters/formatters > (2) standard library > (1) official proposals carry stronger enforcement, while the weight of (4) and (5) varies by context.

Idioms Reveal a Language's Philosophy

The reason idioms differ across languages is not merely that the syntax differs; it is that the mindset each language encourages is different.

Code
Python: Clarity, conciseness, dynamic protocols

C++: Resource lifetimes, value semantics, cost model

Java: Explicit types, interfaces, object model

JavaScript: Function values, async events, object literals

Rust: Ownership, borrowing, explicit error handling

Go: Simple control flow, explicit error returns

Haskell: Purity, types, composition

As a result, directly transplanting the idioms of one language into another produces awkward code.

Code
Translating Java‑style class hierarchies directly into Python results in unnecessary heaviness.

Applying Python‑style duck typing directly in Java conflicts with the type system.

Writing C++ code without a feel for C++‑style lifetime control leads to resource leaks.

Bringing JavaScript‑style dynamic object semantics directly into Rust clashes with the ownership model.

Translating Haskell‑style function composition directly into Java ends up causing an explosion of awkward functional interfaces.

What Makes a Good Idiom

A good idiom is not simply short code.

Code
A good idiom =
leverages the safety mechanisms the language provides,
follows the forms expected by the ecosystem,
and expresses the intent of the code quickly and clearly.A good idiom has the following properties.
  • The reader can immediately grasp the intent.

  • It fits well with the standard library.

  • It reduces hazardous points such as exceptions, resource release, and concurrency.

  • It works well with tools, linters, type checkers, and formatters.

  • It does not create unnecessary abstractions.

How to Learn Idioms

Idioms are difficult to learn from a grammar book alone. You need to read the standard code of the actual ecosystem and ask questions actively.

Code
How to learn idioms effectively:

1.Study examples and self-use patterns in the official documentation.

2.Analyze usage patterns of the standard library.

3.Read small modules from well-known open source projects.

4.Check the forms recommended by linters and formatters.

5.Read authoritative guides like Effective X or The * Book.

6.Always ask yourself: "Why do we write it this way in this language?"

Key questions when learning idioms:

Code
- How is resource lifetime expressed in this language?
- How is error handling expressed? (exceptions, Result, errno, panic)
- How are iteration and collection processing conventionally written?
- What is the unit of abstraction — functions, objects, traits, interfaces?
- Which concurrency and async style feels natural in this language?
- Where are module boundaries drawn?

Consistently applying these questions lets you absorb the idioms of a new language quickly.

Idioms are a qualitative concept, but recent efforts have attempted to extract them quantitatively through source code mining. Allamanis and Sutton (2014) published research on automatically extracting repeatedly occurring pattern fragments from large GitHub codebases using statistical methods, naming the approach "mining idioms from source code."[1]

Such automated extraction can be used to quickly identify idiomatic expressions in a language being newly learned, or as the basis for code review tools that detect anti-idioms. However, statistically frequent expressions cannot be assumed to be idiomatic without cross-validation against authoritative sources.

Cognitive research has also been conducted on how barriers to idiom learning operate in crowdsourced development environments.[2]

Summary

A Programming Idiom is a small code expression convention considered natural in a specific language.

Code
- Paradigm determines the way of thinking.
- Principle determines the criteria for making decisions.
- Pattern determines the solution to recurring problems.
- Idiom determines natural expression.
- Syntax determines the rules for valid expressions.

A good developer is not simply someone who knows the syntax, but someone who can naturally express problems through the idioms of the language. Idioms change over time, their sources are distributed, and they are paired with explicit negative examples (anti-idioms). Being conscious of all three dimensions significantly accelerates adaptation when encountering a new language.

See Also

  • Design Pattern

  • Software Design Patterns

  • Object-oriented programming

  • Programming Paradigm

  • Functional Programming

  • RAII

  • Standard Idioms

  • Idiomatic Code

  • Pythonic

  • Modern C++

  • Idiomatic Rust

  • Composition over Inheritance

  • Linguistic Idioms

  • Cognitive Load

  • Algorithm Skeleton

  • Embedded SQL


Categories: Programming | Programming Languages | Software Engineering | WIKI

Footnotes

  1. Allamanis, Miltiadis; Sutton, Charles (2014). "Mining idioms from source code". Proceedings of the 22nd ACM SIGSOFT International Symposium on Foundations of Software Engineering. pp. 472–483. arXiv:1404.0417. doi:10.1145/2635868.2635901.Allamanis, Miltiadis; Sutton, Charles (2014). "Mining idioms from source code". Proceedings of the 22nd ACM SIGSOFT International Symposium on Foundations of Software Engineering. pp. 472–483. arXiv:1404.0417. doi:10.1145/2635868.2635901.
  2. Samudio, David I.; Latoza, Thomas D. (2022). "Barriers in Front-End Web Development". 2022 IEEE Symposium on Visual Languages and Human-Centric Computing (VL/HCC). pp. 1–11. doi:10.1109/VL/HCC53370.2022.9833127.
Programming Idioms