Modern Programming Languages
- published
- reading time
- 6 minutes
Welcome to my first post of 2025! Hopefully, the quality and frequency of my posts will improve this year.
One thing I hate about modern programming languages is the unnecessary complications they bring.
For example, I started fresh on a new laptop today, and I had to install all the compilers and editors I needed.
This got me thinking: why is installing a language’s compiler or interpreter not enough?
That’s where this list comes from—essential features every modern programming language should have.
Batteries-Included Approach
The first few pages of Head First Python stress the idea that Python is “batteries included,” meaning it provides many libraries out of the box.
Most modern languages follow a similar philosophy, but I believe we need to expand the definition.
Here’s my proposal for what “batteries included” should mean in 2025:
- A bundled or easily installable language server.
- A built-in code formatter.
- Dependency management (like
Cargofor Rust orgofor Go). - An integrated build system that tightly aligns with the language, with bonus points for VCS hooks.
- A project management system to create templates with starter code.
- A test runner that handles all types of tests, including benchmarks and examples.
- A package manager that supports installing and managing binaries or executables.
- A straightforward, secure system for publishing packages or binaries.
- A filesystem structure that keeps my home folder clean.
The following rants are limited to languages I’ve worked with. Other languages may have similar flaws.
Include a Proper Language Server
When I installed Lua through my package manager, I found that setting up its language server was unnecessarily difficult.
Running it on a bare-bones Debian installation was even worse.
For example:
- Lua’s language server (
lua-language-server) often requires manual builds or additional tools to work. - Rust makes it easy with
rust-analyzer, which can be installed with a single command:rustup component add rust-analyzer. - Go provides
gopls, which integrates seamlessly with most editors. - Python, however, has multiple language servers you can use, like pyright, pylsp, and jedi.
- Pyright (written in JavaScript/TypeScript) requires Node.js to install.
- Pylsp (Python Language Server Protocol) is written in Python but requires additional configuration to work with plugins like jedi.
- Jedi is often used directly in editors but lacks some of the features modern developers expect.
The sheer number of options for Python creates confusion, especially for newcomers.
While choice can be a good thing, having a single, official language server would simplify the development experience.
A language server should be included or installable with minimal effort.
Add Formatters to Old Languages
Most older languages don’t include code formatters, which is understandable since they were designed before collaborative coding became mainstream.
However, in today’s world, built-in formatters should be standard.
For example:
- Python: Tools like
blackandautopep8are separate installations. - JavaScript:
prettierdominates, but it requires installation throughnpm. - Ruby:
rubocopprovides linting and formatting but is not part of the core language. - C++: You’ll likely end up using
clang-format, which is part of LLVM but not bundled with the language.
While these tools exist, integrating them into the language would simplify workflows for developers.
Modern Dependency Management
With collaborative coding now ubiquitous, modern dependency management is a necessity.
Many languages include their own solutions, like Cargo for Rust and go.mod for Go.
However, some languages rely on third-party tools, like Python (pip) and JavaScript (npm).
Even within these ecosystems, fragmentation exists:
- Python: Alternatives like
poetryandpipenvattempt to improve onpip. - JavaScript:
yarnoffers an alternative tonpmwith better caching and performance. - Ruby:
bundleris the de facto dependency manager but requires separate installation. - C++: No standard solution exists; developers use tools like
vcpkg,Conan, orCMakefor dependency management.
While flexibility is nice, it often leads to confusion for newcomers.
Build Systems Should Be Standard
Build systems are often overlooked because many developers rely on text editors or tools like make.
However, an integrated build system that leverages language-specific optimizations, code generation, and documentation would be far superior.
Languages like Java eventually developed tools like Gradle and Maven to fill this gap, but they remain separate installations.
Rust integrates its build system (Cargo) directly into the language ecosystem, which is a far better approach.
Project Management Tools
This ties into the above points but focuses on creating template projects.
Frameworks and plugins often handle this, but why not include it natively?
For example:
- Rust:
cargo newcreates a project with boilerplate files and directories. - Go:
go mod initsets up a module with all required metadata. - Python: Django and Flask provide project scaffolding tools but are specific to the framework.
A universal, language-wide project management system would be a game-changer.
Tests Need Running
Every language should include a comprehensive testing framework with a great test runner.
It should support all types of testing—unit, integration, fuzzing, and benchmarking—and provide clear, detailed results.
For example:
- Rust:
cargo testis built into the ecosystem. - Go:
go testsupports testing as a first-class citizen. - Python: Tools like
unittestandpytestare great but require separate installations. - JavaScript: Popular libraries like
JestandMochadominate but are not bundled with the language.
Include coverage tools and fuzzing support, too!
Binaries and Packages
Package managers like pip, cargo, go, and npm allow for the execution of binaries and executables, but some implementations are lacking.
For example:
- Python: Tools like
pipxemerged to solve the problem of globally installing CLI tools without polluting the main Python environment. - JavaScript:
npxruns binaries temporarily without polluting your environment. - Rust:
cargo installplaces binaries in a predictable~/.cargo/binfolder. - Go: While
go installallows fetching binaries, the installation defaults to$HOME/go/bin, cluttering the home directory.
Languages should adopt predictable, clean methods for handling binaries and dependencies.
Publishing Should Be Easy
Publishing packages should be secure yet straightforward.
I shouldn’t need to install additional tools just to publish.
For example:
- Python: Once had multiple ways to publish packages (
setup.py,distutils,wheel). Now, tools likepoetrysimplify the process, but it’s still not included with the language. - Rust:
cargo publishmakes publishing seamless. - Go: Publishing binaries requires manual setup, often involving GitHub releases or custom scripts.
Languages should aim for simplicity and stability in their publishing ecosystems.
Keep My File System Clean
Languages should avoid cluttering my home folder with cache, packages, binaries, or other files.
For example:
- Go: Creates a
~/godirectory in the home folder, often owned byroot. - Python:
pipsometimes leaves behind unused files in the home directory or environment. - Rust: Places binaries in
~/.cargo/bin, which is a good example of organization. - This is unnecessary when proper standards like XDG (
~/.cache,~/.config,~/.local) exist.
Follow the standards, keep my home folder clean, and make me happy.
Conclusion
That’s the end of my rant. If you’re building a programming language, please consider these points!
Modern programming languages can reduce developer frustration and improve the experience with thoughtful design and inclusion of these features.