Ask HN: What are some of the most elegant codebases in your favorite language?
Your favorite language can be anything - Lisp, Python, C, Haskell, etc.
Which codebases are the most elegant ones written in your favorite language that new comers to the language can learn something from?
185 comments
[ 97.8 ms ] story [ 522 ms ] threadAs someone who has written many small apps in PHP, I might dive into it and finally see what that Laravel thing is all about.
"Reasonably readable" is what I strive for nowadays, and a key aspect is that I no longer think there is an objective measure for it. It depends on the team/company you're in, and its important to keep in mind that the goal is to effectively communicate the program to them.
Anti-patterns are things like using a wrong data structure, using the wrong levels of abstractions, tightly coupling components so they are not composable, leaky interfaces, god objects doing too much
The strict definitions of many of these can be subjective at the edges, but they all objectively exist and cause fragility, instability, inflexibility and maintenance problems in the systems built from them.
Clean code is code that’s easy to read, has the right level of abstractions, uses the right data structures and has the minimal possible anti-patterns in them (ideally zero!) above.
Calling these things bogus is unfair, I grant they can be subjective in some places but they are prevalent in poorly designed systems especially by junior engineers.
Wikipedia page on anti-patterns: https://en.m.wikipedia.org/wiki/Category:Anti-patterns
There are project management anti patterns too https://en.m.wikipedia.org/wiki/Anti-pattern
Anti patterns are real. Clean code is real. If you cannot see that, I would suggest reading a lot more codebases from open source projects (pick medium and large codebases, not trivial programs) and after 10 or so you will see obvious structural and quality differences, as well as hundreds of anti patterns and examples of clean code.
I'll back this up with the example of Magento 1. It's a PHP e-commerce platform that has (at first glance) some of the cleanest code you'll ever see in PHP. It looked like the entire development team had come from a Java background. Every component was neatly divided into a PHP object with documented private and public methods. There was a clean MVC separation, with lots of shared behavior abstracted into "Helpers", and a Theme system handling the rendering of the front-end. I have no doubt that the authors of Magento prided themselves on how clean their code was. For them, having spent full-time careers structuring all the many features of their platform into these files, it probably did feel like a clean organization. And when I initially reviewed the code to evaluate using Magento, I also thought it was clean and was part of my choice to use it.
The problem was that when you take something as complex as a full-featured e-commerce system and modularize it so thoroughly, you end up with a literal 7000 classes, and it becomes impossible to figure out the sequence of execution of a single page request through that massive tangle. Is it "clean code" if I have to add debugging print statements to 57 source files to figure out the path of execution that renders a single page?
If I had to choose a circle of hell where I would edit Cyrus IMAP (the worst C code ever) for the rest of eternity, or edit Magento for the rest of eternity, I think I would actually choose Cyrus.
What is a "wrong data structure"? Is it wrong to do linear scans when hash access would be sufficient?
> ... using the wrong levels of abstractions
When is the abstraction level wrong? I can name you at least 10 projects that avoid so called anti-patterns by introducing unecessary abstractions. Is something really an anti-pattern if it's "solution" massively reduces your development velocity but now everything is nicely composable?
In my opinion, anti-patterns are either obvious (constantly using different names for the same thing) or totally subjective to the context (when to repeat yourself).
But maybe I'm not able to put myself into the shoes of novices and a lot of things seem obvious to me. Granted.
I've seen people use multiple named arrays, search for something in the first array, then use its index to find matching data in the other arrays, instead of a dictionary of string:tuple.
Another example is languages with arrays and vectors, usually one or the other is preferred. In Go you can use arrays but you're supposed to use slices whenever possible, while the opposite would be appropriate in, say, C.
It is and it isn't. It does opinionated ways of doing things. But IMO what makes it so great is just how easy it is to opt out of the opinions you don't like. Facades are an excellent example of this: don't want to use them? Well, you don't have to (and IMO a really big app that may be a good choice - in a small app it likely won't matter and you might as well use them).
I also really appreciate FakerPHP for how easy it is to generate realistic fake values.
Even with the relative magic of things like init functions that execute on import, there's so much less magic that you have to consider.
IMO it's the easiest to jump into for this reason. Yes, the code itself might be excessive, but reading Go is as simple as can be.
I know it's not everybody's cup of tea, and that's fine. There's room for different languages in the world. But I'm knocking out useful utilities that make a difference for my team and business in days or hours, and supremely uninterested in code golf, so I don't personally agree with the criticisms.
BASIC -> VB -> Java -> Python for some time -> Go
How you feel about Go is how I feel about JavaScript
And I like Typescript to some point.
I do not think they languages are mutually exclusive (on the like scale). But I am just an amateur hobbyist dev.
1) Use the pointer receiver unless you have a really good reason to do otherwise.
2) Never, ever use named returns. Code can (and should) always be written to not use them.
Want to make a function or variable or field public? Just capitalize it. That’s it.
Error handling? Check if the error isn’t nil and handle it, otherwise move on. No need for silly things like try except.
And there are no magic functions because everything is explicit.
Go is just simple and pleasant to use.
So a “good” language depends on what you’re trying to do with it.
At my employer we use a subset of C++ and we use Go. We have a process by which when you write code others have to review it and you need to have a review by someone who is an "expert" [0] in the language [1]. You become an "expert" by being ordained by a shadowy committee who reviews code you submit to the code repository. They look for knowledge of the core language, how this language is used within the company, the external and internal libraries, performance, testing, etc. There are many factors and if you demonstrate all of them you get marked as being this type of "expert."
Before I joined this company I had written code which was launched into production in the following languages: Java, PHP, C, C++, assembly (arm + x86), Python, JavaScript (browser + node), and a few more. I would consider myself about average in all of these. I did not write any golang.
After I joined my current job I obtained this "expert" bit for both C++ and Go. It took ~1.5 years to get the C++. It took ~3-5 months for Go.
You can actually see the first golang code I wrote (this was at home when I was experimenting with the language before convincing some team members we should rewrite some of our infra in go): https://github.com/gravypod/gitfs
It has all sorts of mistakes but I didn't read any guides. I just used https://cs.opensource.google to search for examples of things that I thought I would need to do (ex: "how do I do a switch statement").
For C++ I had worked on ~2 projects that used it. My first few PRs were very rough and I had a lot of performance issues that crept into my code. If I didn't already have a lot of C experience I would have also had a bunch of pointer/lifetime stuff the reviewers would have found (I know this from reviewing new team members first lines of C or C++).
I know that to some C++ represents a literal worst case but most people coming from C++ to Go will think it's amazing because it removes all of the common footguns.
> Things like awkward error handling
Yes, it is very clunky and annoying but it's very simple. It's just a normal if statement and doesn't use any special language rules. I think this would still be better if there was dedicated syntax for it (something like throw/try/catch which works the same way but gives it a different syntax) but honestly I don't think it's as bad as it's made out to be. It's basically a "less bad" errno and that worked go-... dece-... ehm fine for many years.
> dealing with null
I've never really had to think about this too much. There are some times it is important but I rarely return nullable things without errors and it hasn't bitten me yet. My code is not provably correct but for the things I'm working on I don't need that guarantee. If I did I'd probably switch to Rust or something with better type safety.
> capitalization for public/private
Yea, this sucks but it doesn't really get in my way much. I don't like it but it isn't actively hurting my usage.
> magic functions
Like `String()`? I don't know if that's the worst think in the world. Python, C++, and Java have things like this.
> and imports
It's not been too bad for me. What I think is unfortunate is that the package of something is unconnected to where it is in most go usages which is annoying but it makes things more terse. This does actually hamper my productivity.
> I don't think it's good or easy to use at all, but people rave about it.
It's pretty easy to use because all of the libraries I've seen share common interfaces for behavior. This makes things feel a lot more cohesive. fuse-go and billy was very easy to use in gitfs because of this.
[0] ...
What gets me about go is 2 things.
Its touted as being so easy that a junior dev can pick it up easily; it's targeted towards average developers. Fine, this is a virtue; when Java said the same thing, it's a horrifying blight.
The other is that people seem to mistake motion for action. "The code flies from my fingers" is one way I've heard it said. And, sure, you have the same error catching boilerplate (which again, in go is a virtue but other languages seems to be considered a sin) 1000x, as if the clicky-clicky of the keyboard is some measure of productivity.
It's got a big "blub paradox" thing going, to my eyes. I don't find it elegant at all, or maybe I mean not very expressive.
Taken ad absurdum, I have a language that only adds 1 to a variable. I can understand that language in milliseconds. But I can't do much with it. Maybe go hits a good balance, I dunno, but I found it quite tedious.
Very satisfying to be able to say "nope, it's fine, here's the source".
R - sf package is a clean example of functional OOP
Python - pytudes (Peter Norvig’s notebooks)
Haskell - Elm compiler. I could mostly understand what’s going on even though I barely know any Haskell.
Ruby - Sequel is really nice.
Rust - Ripgrep
Pretty much any F# codebase is super readable too.
Funny because I detest it because of how difficult it makes it to dig into and customise spatial data and visualisations at a low level like I am used to with the spatial packages it sort of supercedes
https://github.com/desktop/desktop
P.S. It's now easy to check a repo file by replace .com with .dev: https://github.dev/desktop/desktop
Full DDD, it was a refactor during COVID-19 for my ecommerce.
It powers https://belgianbrewed.com
Copy paste from: https://news.ycombinator.com/item?id=35257225
> But I believe the project is much cleaner and frankly better to understand than all other projects i've encountered for this size. I'm using DDD, so DDD knowledge is a requirement to navigate this in a breeze :) :
- https://snipboard.io/D03VWg.jpg - General overview of the architecture. Small fyi: Connectors => Autogenerated nugets to call the api's
- https://snipboard.io/9M24hB.jpg - Sample of Modules + Presentation layer
- https://snipboard.io/ybp6EH.jpg - Example of Specifications related to catalog ( = products )
- https://snipboard.io/lE9vcK.jpg - How specifications are translated to the infrastructure ( here I'm using EF, so I'm using Expressions a lot), but plain old SQL is also supported. A query is basically a list of AND/OR Specifications where a hierarchy is possible. This would translate to "(QUERY 1 ) AND ((QUERY 2) AND (QUERY 3))" in the Infrastructure layer.
- https://snipboard.io/7rVBpk.jpg - . In general, i have 2 List methods ( one for Paged queries and one not for Paged queries)
Additional fyi: Is V2, so has some legacy code. Uses my own STS. Has 2 gateways ( the ShopGateway that is used to develop new sites and the BackendGateway for the Backend). Enduser frontend is in MVC for SEO purpose, Customer backend is in Angular ( SPA). The basket is a NoSql implementation on top of SQL server.
The enduser frontend supports a hierarchy of themes ( so it's insanely flexible to create variations of pages for other clients).
There are more projects involved outside of this solution, eg. nuget repo's usable accross solutions (JWT, Specifications, ...) and "plugins" for a standalone project that is installed for end-users for syncing local data. So it's +101 projects :)
Edit: my specification implementation has been open-sourced here: https://github.com/NicoJuicy/Specification-Pattern-CSharp
Someone was curious to see it.
- "Oh, really? Who wrote it?"
"I did."
..."Lessons in humility"
Now lets see Paul Allen's screenshots of the directory structure of his codebase.
Ok, i get it.
Just a shame that while i gave a pretty clear technical description on the "why i think so".
Nobody of the downvoters actually cared to give a actual technical counter argument or even did a test with a technical question... Instead just a reply with a partial movie script...
Ugh.
Pretty much anything else from Edi Weitz is also great.
Mezzano is quite elegant in my opinion, especially for an operating system. For example, this is the USB mass storage driver: https://github.com/froggey/Mezzano/blob/master/drivers/usb/m...
For example: //============================================================== // Ajqvue Constructor //==============================================================
The constructor definition is already clear, I don’t need a big header telling me what a constructor is.Instead, use comments generously where needed - for example, if you have a piece of business logic, a comment explaining the intent behind it can help the maintainer (likely you) a year down the road. What I mean by this, is that what your code DOES should be self evident - but documenting the business decision behind the implementation has done wonders for me.
There are undoubtedly some pointless comments, but there are some good ones too, and the code is understandable. I think the sibling comments are being overly harsh and are zeroing in on single examples and missing the forest for the trees.
I suspect recommending your own code as an example of the most elegant codebase is triggering to others, so there's probably a negative initial reaction and probably a lot of people clicking your link so they can point out why you're wrong
https://github.com/janetacarr/quadtree-cljc
Each language has different ways that may lend itself as part of this shaping, but I like being able to think of this as something to look for when reading and strive for when writing, irrespective of the language at-hand.
C - Redis
In Perl, everything serializes to JSON without fuss. It can be "lossy"; objects without an explicit JSON serialization method and coderefs can't be deserialized, but serializations at least have placeholders for them.
Compare to Python's json module, which doesn't try very hard to serialize things and throws exceptions every time it runs across something new. It's very frustrating to use.
Perl's DBI provides a universal API for all databases, with implementation-specific details in an underlying DBD module (which both provides glue between the DBI abstraction and programmer access to features specific to different database systems).
Compare to Python, where you need to import a different module and use a different API for every different kind of database. As I increasingly use Python for a living, I frequently wish they'd follow Perl's example with this.
```rb
```This is pretty close to "built in", I think.
I ended up going deep on the D programming language for my "new language". As a statically typed language it's not as expressive as the Python/Ruby/Perl contingent, but it's a lot more expressive than most static languages, and its C-like syntax "clicked" with me in a way Ruby never did.
It also can approach C-like run-time performance and memory footprint at times, which I appreciate. As much as I like developing in Python and Perl, I frequently wish they had better run-time performance and a smaller memory footprint. D gives me that, at the cost of a little expressiveness.
Go has this too, very handy:
https://godocs.io/database/sql
You... don't. Python has a unified DB api (DBAPI 2.0) which drivers usually support. They often also provide a lower-level API specific to the DB.
I usually end up always wrapping the json_decode in an eval to catch the error and handle it in an easier to understand fashion.
But I agree though, its nice how json and perl data objects pretty much map to each other. It's great.
> Compare to Python's json module, which doesn't try very hard to serialize things and throws exceptions every time it runs across something new. It's very frustrating to use.
Sounds kinda backwards to me. I thought the fact that throwing exceptions (or "making a fuss," if you will) is better than silently producing incorrect (or "kinda lossy," if you will) results wasn't controversial anymore in 2023.
And then, the opposite is Monix (https://monix.io/) also written in Scala. It's also about reactive streaming and the API is great, but the internal code is ugly because it sacrifices readability/composability for performance.
C
* Lua https://www.lua.org/source/
* Redis https://github.com/redis/redis (mentioned twice)
* Ruby https://github.com/ruby/ruby
* SQLite https://sqlite.org/src/dir?ci=trunk
C++
* Botan https://github.com/randombit/botan
* ClickHouse https://github.com/ClickHouse/ClickHouse
Go
* Go's standard library https://cs.opensource.google/go/go
* HashiCorp repos, particular those by Mitchell https://github.com/hashicorp
F#
* jet.com repos https://github.com/jet?language=f%23
Haskell
* Elm compiler https://github.com/elm/compiler
Lisp
* CL-PPCRE https://github.com/edicl/cl-ppcre/
* Mezzano USB driver https://github.com/froggey/Mezzano/blob/master/drivers/usb/m...
PHP
* Carbon https://github.com/briannesbitt/Carbon
* Laravel https://github.com/laravel/laravel
Python
* pytudes (notebooks) https://github.com/norvig/pytudes
R
* sf (ex of functional OOP) https://github.com/r-spatial/sf
Ruby
* Sequel https://github.com/jeremyevans/sequel/
* Sidekiq https://github.com/sidekiq/sidekiq
Rust
* ripgrep https://github.com/BurntSushi/ripgrep
Scala
* fs2 (ex of good type safety) https://github.com/typelevel/fs2
* monix (ex of ugly code with great performance) https://github.com/monix/monix
TypeScript
* GitHub Desktop (ex of SPA) https://github.com/desktop/desktop
---
thanks to: cejast, bb86754, wewxjfq, antonyt, bit, diego_moita, agjmills, freedomben, Contortion, valenterry, DethNinja, mberning, seneca
An old one, but the FTGL library (renders truetype fonts in old-school OpenGL in half a dozen different ways: texture-per-letter, texture-per-word, 2D polygons, 3D polygons, etc) is the best example of C++ inheritance I've ever run across. The code formatting isn't my favorite, and comments are sparse, but the hierarchy of objects subclassed for all the different rendering modes is just about perfect. https://sourceforge.net/p/ftgl/code/HEAD/tree/trunk/
Perl
The Mojolicious / Mojo toolkit. Great minimalist API and great documentation and clean code all around. https://metacpan.org/release/SRI/Mojolicious-9.33/view/lib/M...
https://github.com/solvespace/solvespace/blob/master/src/srf...
There is a lot more in other files - triangulation, booleans, creation - but the core math functions are there in very readable form.