# An Ode to Header Files

DevFeed: [An Ode to Header Files](<https://devfeed.tech/articles/an-ode-to-header-files-21138.md>)

Original publisher: [Read original article](<https://blog.reverberate.org/2025/01/27/an-ode-to-header-files.html>)

Author: Haberman

Published: 2025-01-27T00:00:00Z

Content type: article

Language: en

Sources: [Josh Haberman](<https://devfeed.tech/sources/josh-haberman.md>)

Topics: [C](<https://devfeed.tech/topics/c.md>), [C++](<https://devfeed.tech/topics/c-plus-plus.md>), [Software Engineering](<https://devfeed.tech/topics/software-engineering.md>), [modules](<https://devfeed.tech/topics/modules.md>)

Tags: [blog-post](<https://devfeed.tech/tags/blog-post.md>), [c](<https://devfeed.tech/tags/c.md>), [c-plus-plus](<https://devfeed.tech/tags/c-plus-plus.md>), [compiler](<https://devfeed.tech/tags/compiler.md>), [software-engineering](<https://devfeed.tech/tags/software-engineering.md>)

## AI overview

The article argues that separating a module's public API into dedicated header files remains beneficial for human-readable software engineering, even though newer languages can support separate compilation without traditional textual inclusion. It proposes hygienic, modular headers containing only public API declarations and no implementation details.

## Source excerpt

C and C++ have a somewhat distinctive feature that almost no language since has decided to replicate, which is to put public API declarations into separate files called header files: // square.h: defines public API void SquareArray(int* p, size_t n); // square.c: defines implementation // Internal-only helper. static int SquareNumber(int x) { return x * x; } // Implementation of public API. void SquareArray(int* p, size_t n) { for (size_t i = 0; i < n; i++) { p[i] = SquareNumber(p[i]); } } More "modern" languages almost universally choose to collapse header and source into a single file, where public functions are marked in some special way: // Rust: functions are exported with "pub" fn square_number(x: i32) -> i32 { x * x } pub fn square_array(arr: &mut [i32]) { for i in arr.iter_mut() { *i = square_number(*i); } } // Java: functions are exported with "public" class Square { static int squareNumber(int x) { return x * x; } public static void squareArray(int[] arr) { for (int i = 0; i < arr.length; i++) { arr[i] = squareNumber(arr[i]); } } } I think this move away from header files is unfortunate. Separating public API declarations into their own files offers many benefits that cut to the heart of good software engineering practice. In this blog post I will articulate what these benefits are. My hope is that modern languages might consider adopting something like header files (a few do to some extent, which I will explain later). An Obsolete Mechanism, Repurposed You may find it strange that I would advocate for header files, given that they are effectively obsolete, at least compared to their original purpose. Header files were initially designed to solve a technical problem for the compiler, which is how to share macros and function declarations between translation units in a way that supports separate compilation. But newer languages have convincingly demonstrated that separate compilation can be achieved without header files, and especially without the primitive