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
Cargo
for Rust orgo
for 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
black
andautopep8
are separate installations. - JavaScript:
prettier
dominates, but it requires installation throughnpm
. - Ruby:
rubocop
provides 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
poetry
andpipenv
attempt to improve onpip
. - JavaScript:
yarn
offers an alternative tonpm
with better caching and performance. - Ruby:
bundler
is the de facto dependency manager but requires separate installation. - C++: No standard solution exists; developers use tools like
vcpkg
,Conan
, orCMake
for 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 new
creates a project with boilerplate files and directories. - Go:
go mod init
sets 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 test
is built into the ecosystem. - Go:
go test
supports testing as a first-class citizen. - Python: Tools like
unittest
andpytest
are great but require separate installations. - JavaScript: Popular libraries like
Jest
andMocha
dominate 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
pipx
emerged to solve the problem of globally installing CLI tools without polluting the main Python environment. - JavaScript:
npx
runs binaries temporarily without polluting your environment. - Rust:
cargo install
places binaries in a predictable~/.cargo/bin
folder. - Go: While
go install
allows 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 likepoetry
simplify the process, but it’s still not included with the language. - Rust:
cargo publish
makes 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
~/go
directory in the home folder, often owned byroot
. - Python:
pip
sometimes 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.