Good code is like a love letter to the next developer who will maintain it
addyosmani.com
addyosmani.com
I don’t think I’d do much differently in retrospect, except be far more cautious about where and who I spend time around.
Even this very thread has some pretty bad vibes: “who can say what good code is?” “good code can’t exist with deadlines.”
I feel really bad for junior/intermediate devs that read this stuff and internalize it.
What Go does is encode solution complexity implicitly throughout a code base. This is the same effect JavaScript often has.
But of course it's still possible to make a mess: I've seen someone who created an ad-hoc object model (with its own structs, plus multiple inheritance and all) on top of Golang and wrote a few apps in that style. But not having the features in the first place can help.
Abstraction is the only tool we have to even hope to manage complexity. Unfortunately there is no way to force good abstractions, finding out the perfect abstraction is the deal of the art. Maybe I’m wrong on this, but I’m on the elitist view that the industry really shouldn’t try to replace a single senior developer with 10 juniors that monkey patch based on stackoverflow/chatgpt. I don’t even see how that makes financial sense, if one values product quality.
There are no automated to enforce good abstractions, but languages and tools can serve as a way to shepherd programmers into avoiding or preferring certain patterns. This is an argument as old as the programming language field itself.
For some people Golang strikes a good balance. I'm not one of those people, but I have learned to respect it.
I do agree with you to some extent about not replacing senior engineers. But a "bad" senior engineer who is "too smart" for their own good can do more damage then juniors following a template.
All things in balance for sure.
_Every time_ the programmer responsible seems blissfully unaware of the possibility of said error and I need to provide a test case for them to reproduce it. Their mental model of the code they think they wrote is wrong compared to the actual code.
The culprit is usually something like an `interface{}`, granted, which people then excuse as being un-Go-like... but if all the language's supposed ease and robustness fails as soon as it encounters anything outside of its own ecosystem, then it's not worth much. All the busywork around errors starts to look like a cargo cult to make the juniors feel like they're accomplishing things when they're just glueing things together.
No other language I've seen has it so bad. Rust is particularly good in this area, and seems to view it as its own responsibility to interoperate cleanly, providing sane and powerful ways to opt-in/out of Rust's semantics at the edges. Even TypeScript makes it easy to partially or gradually type when you need to, and this seems much safer in practice to me.
Kindly written rust is fine, same goes for a lot of languages. The issue is, sometimes really smart people have no interest in kindness. There are a lot of social factors that go into people writing monstrosities "gotta look smarter!" "Gotta have job security!" "Don't want anyone to call me out on this".
I write dumb code 99 percent of the time. I'll never get promoted for it, but people know exactly what it's doing.
Blame often gets passed upwards to some product manager or sales coming up with some outlandish business requirement, but honestly the real reason is that the code wasn't that flexible to begin with.
Which is perfectly fine, not all code can be flexible and infinitely reusable. Perhaps we should stop forcing every single file to be.
Very rarely is that good code, even if you actually do understand the design. Most of the time, when you come to extend it, it turns out to be in a slightly different way than the original author had expected. Now you've got the complexity of solving the original problem (inevitably) and the new problem (also inevitable) and also some other problem that might have needed solving but didn't after all (not inevitable).
Most of the time, code that is easiest to modify or reuse is the code that does its current job in the simplest way possible. That doesn't mean you can't create reusable abstractions - doing so often does make the code simpler even for just its current job. And that usually ends up aligning quite nicely with what turns out to be (almost) what is needed for other tasks anyway.
> Most of the time, code that is easiest to modify or reuse is the code that does its current job in the simplest way possible.
Very much this.
Well, you rarely know, more often "guess".
There's an important distinction between public and private interfaces. With public interfaces, I contend that it does make sense to think about future use cases and put in some flexibility even at the cost of a small overhead.
I've had several people comment on the quality of my code. For a FOSS project. Of a command-line tool that nobody cares about.
People notice.
But yeah, this article is too much fluff because people don't see it that way.
Half of the people who commented on my code mentioned the snarky comments I wrote.
https://blog.desdelinux.net/en/el-open-source-con-malas-pala....
As said there, this might as well be simply a correlation with commented code.
Or, people swearing might be an indication of having taste.
I encountered their code while debugging weird behavior in a legacy feature. The code structure was immediately intuitive and comprehensible. Just the right amount of detailed but not superfluous commenting. Beautiful. It made my day so I let them know!
I never get this sentiment from developers. I've had reviews were developers wanted me to slim down & rewrite comments.
It's often hard in the moment to understand what's obvious or not. Just always writing a comment even if it's obvious can save a lot of pain in the future.
//Returns the user's full name return user.email;
Edit: if even 30 seconds
Now, if you're already changing that part of the code/file, then sure it's pretty low overhead. But for something as simple as that function, I'd probably delete the comment entirely.
My company is heavily regulated (finance) and highly bureaucratic, but I still just merge those PRs without waiting for the CI or without even bothering clicking approve, I just look at it. When I find one myself I just merge to master or edit in Github directly. No auditor has ever batted an eye to that.
The issue is that most things are obvious when you write the code, but that it's often not clear what's not obvious in the future.
So if you just get in the habit of explain every function it will lead to a more understandable codebase.
Yes, but that's not all that annoying when the opposite is you stumbling upon a service pattern with uncommented methods along the lines of CVKUser getCVKUser(int cvkId, ...) or VFUser getVFUser(int cktId, ...), without having any idea what the person writing the code 4 years ago was thinking.
You've no idea what CVK or VF is supposed to be and have to spend an hour looking at both of those classes, as well as everything that surrounds them in the codebase, as well as what cvkId and cktId are supposed to be and why cktId supposedly matches VFUser, to do enough code archaeology, so you're able to figure things out confidently.
That said, there is little use in code comments that explain WHAT the code does for trivial bits of code, instead of comments that explain WHY it works like that (like a more sane version of having to dig through Jira tickets, or years of commit history, or old pull requests, or whatever) or what other considerations there are to take into account (since separate docs won't be read as much and Jira tickets won't always have the technical information).
It happens. Even worse is when the code has been updated but the comments have not. I've picked up the habit of double checking comments with git blame.
I have had extraordinary coworkers who wrote incredibly solid code that was a joy to work with at every turn. When some of those coworkers have moved on, I've bought them thank you presents and taken them out for a meal to say thank you for making a difference. It's a token gesture in the face of how much I've learned from them and their wonderful work.
My message is to all of us who work away in the trenches of our day-to-day, where we stare through straws and move grains of sand; please let us shout and celebrate the granules of gold that our peers produce!
Imagine a carpenter that says straight cuts aren't possible, just because they never maintained their tools and the last 10 cuts came out wrong.
Of course good code is possible. But not every person is going to write it and it is not going to happen under all possible circumstances and in all given project structures.
Our job as professional software people is to be able to judge how to create the circumstances and the time frames under which good code can be written — and to say No to projects that cannot be written by us and our team.
Anyone who's worked on a construction site would agree that this is not too far from reality.
I agree, it's quite sad, but that sentiment usually comes with burnout from too many quick deadlines and vast piles of tech debt.
That would be my question. We are not talking about people saying that within their context good code is not possible, we are talking about people who say good code is not possible, period. Maybe burnout has to do with it, maybe also depression. But I even if I was stuck in a job that made me churn put half broken code all day I'd still remember my hobby projects in which I experienced myself that perfectly fine code can be written by a single determined person — and quite frankly: it doesn't even have ro take that much longer, it just has to be a trusted, concentrated low pressure environment.
Imagine a carpenter that says straight cuts aren't possible, just because they never maintained their tools and the last 10 cuts came out wrong.
Of course good code is possible. But not every person is going to write it and it is not going to happen under all possible circumstances and in all given project structures.
Our job as professional software people is to be able to judge how to create the circumstances and the time frames under which good code can be written — and to say No to projects that cannot be written by us and our team. This isn't always a simple thing, but it must be done.
The saddest aspect is that you see many experienced software developers who never experienced things working well. And as someone who sometimes writes code for fun I cannot understand that at all. What I get is that throughout your professional life you can never had the chance to be in a project where you could write good code. However you can write good or even perfect code at home where nobody pressures you. And if it is possible there, why wouldn't it be in a professional context?
Yes, not every programmer cares about good code. But of those that do, also not all of them can agree on what good code is.
And not all good programmers can write good code, for one reason or another.
It is way less black and white than good vs bad/mediocre programmers.
Some of these are not like the others. Not allowing a carpenter to use the most suitable tools is petty. Not allowing them 30 days may make sense depending on the business context. What if it's a stool that's going to be sat on once and then thrown away? Or there's only 10 days salary in the bank but then an invester will see it and may pay for 100 days work?
Good code usually takes more time than bad code (depending on the reason!) and often it is worth that extra time (and the fight to get it). But sometimes it's better not to and that's OK.
It’s all about trade-offs. Which many developers don’t - or just refuse to - understand.
My point was that without some context this kind of conversations - about good and bad code and good and bad coders - are not very useful.
Personally I believe good code is an reliable, maintainable, transparent and well designed solution for a problem (or a set of problems). And that means for a small problem aome ad hoc script can be good, while for a big complicated set of problems an elaborate well coded project with a build system can be good code.
But what I believe is that there are ideal solutions from an engineering standpoint, and there are ideal solutions from an business standpoint. And those sometimes don't align with each other. E.g. when you solve a core problem that should really be dealt with properly with a quick fix that might be a cheap way to reach the short term business goals, but it could shape a lot of engineering decisions in the future in a bad way and maybe even become a business problem in the long term.
What I found remarkable wasn't that there are badly managed projects, but that there are software devs who seem to think there is no other way than doing it like that.
I think the abundance of jobs - maybe not anymore? - also added to the problem.
I’ve received countless CVs where the developer didn’t stay at the same job for more than a year, max two. Now, it could be that some of the jobs sucked, bit it is highly unlikely that all of them sucked.
With food, at least there is a wider agreement of what consists of good and bad.
[1] https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...
On the other hand, I find that someone saying that "universally good code" is not possible as understandable.
I personally believe it is only really achievable in certain domains, for some others (especially for code handling a lot of essential complexity) it is borderline impossible to not have at least some uneven corners. And there's only so much you can sweep under the carpet of indirection.
After nailing a couple of them together, it has to send them to another far far away land, for some glue job.
Afterwards he gets them back, he applies varnish over the wood pieces, and sends them yet again to another far far away land for the paint job.
Those people on the far far away lands don't have any carpentery training and do whatever they feel like to meet the description of what they are supposed to deliver back.
While the carpenter tries to rescue what is possible from each delivery, so that the table and chairs only look half as bad, people can still seat on them without falling, and the tables have four legs about the same size.
I just took apart a bunch of stuff on a VW van: the bus itself was nominally manufactured in Poland. According to some of the labels the guts of the servo that I took apart and repaired was made in Switzerland. But the servo itself was made in Germany. When you look at the guts parts of it were made in China, parts in Japan and probably some parts in Switzerland but it's unclear which those would be.
The parts of that car have collectively already traveled more than the car ever will by the time it gets delivered to the customer (in the Netherlands).
After I graduated college in 2004, they hired me at 50k.
I didn’t know my value.
which is one the important point yet a very difficult information to get
what is mind blowing is how ignorant but somehow subconsciously proud (dunnig-kruger kind of way) will bully themselves into higher salaries (I've seen them repeatedly) while the good working, caring craftman will not and stagnate at lower pay.
worst of all is when the low skill guy becomes your boss..
Never saw them try.
So while I do try to write good code, if you are looking for appreciation, it comes from what the code does, not how it is written.
Another angle here is that code has become seen as something with the shelf life of a banana. People today talk of code merely a year old as legacy that needs to be rewritten ASAP. If one grew up in a world where code is scheduled to be thrown away as soon as it gets to production, I guess I can see how quality of code and documentation doesn't matter at all.
That's not a happy world though.
I grew up with the codebases of Unix (particularly SunOS, later Solaris). Code lives on for decades. One matures it to perfection and then it's perfect for a long, long time. That's a much more pleasant way to honor the craft.
As a codebase outlives its best-practices, do you stick with them and extend with those same anti-patterns or do you implement new things with a different mindset than the rest of the code?
-- If you break with tradition then you are making it harder to understand the codebase as a whole.
eg: why do we have a mix of procedural/OO/functional/... methodologies with spaghetti code at the core?
-- If you refactor everything then you are definitely breaking something.
eg: you have a 10M+ line java codebase with transformations bringing you into the 2020s from coders who originally knew C and Java 1.4.
-- If you keep with tradition then are you writing the best code possible?
eg: would you willingly use goto of some variant in new code if you jumped into a Cobol 74 codebase?
I think what the article really wants to talk about is "clear" code or "understandable" code and not "good" code.Something could be written using Java 1.4 idioms with good organization and factoring, or using the latest functional hoohas with everything all mashed together. Or the inverse, of course.
If the structure of the code matches the structure of the problem, the implementation style is a much smaller deal.
* Modern, where the language's features and idioms reflect the best way we know to write good, clean code today.
* Compatible, where code written in the language years ago continues to run the same as when first written.
* Simple, where the language has relatively few concepts that compose in clean ways and where there are few ways to accomplish the same goal.
But you only get to pick two.
If it's the latter, I need more info please!
definitely not true. this is good code:
https://github.com/torvalds/linux/blob/master/fs/file.c
this is good code:
https://github.com/golang/go/blob/master/src/strconv/atoc.go
its formatted, commented, direct. the fact that you struggle with writing time tested code does not mean that everyone does.
From my personal perspective this is mediocre code at best.
It’s written in an unsafe language.
Littered with macros, which in C are not hygienic and are land mines in waiting.
Constants defined with lowercase and not actually marked as const.
Double underscores everywhere, which are the bad alternative to namespaces seen in weak languages like C.
WTF does “BITBIT_NR(nr)” do!? Does it “bit” the “bits”? What is NR? Is it a “number”? Then why not use “n” and drop it from the macro name?
Abbreviations everywhere: ofdr, nfdt, fds, fs, etc…
Then there is a long-winded explanation of how they pack bits into an array of longs. Okay, why not make this a reusable module of code? Because C is a weak language, or because the Linux kernel is spaghetti with a dozen implementations of bit maps?
\*
* Note that this can drive nr *below* what we had passed if sysctl_nr_open
* had been set lower between the check in expand_files() and here. Deal
* with that in caller, it's cheaper that way.
Huuuurk.Sorry… did I just see a data race just casually commented as “okay because it is faster if it’s horrifically unsafe?
Yes? No? Maybe?
Do you even know?
Finally a sane person <3
Honestly, I never understood why so many developers abbreviate function names or variables. Is it so much pain to type abc<tab> for auto completion?
Why not simply name fd file_descriptor?
Short names are a mental barrier and a level of abstraction that can easily be avoided.
users.map(u => u.lastName)
Nobody is going to have any questions about what 'u' is here. Do that in a codebase that has a 100% consistent and very frequently used User type, and it starts to feel quite reasonable to just use 'u'. It's as familiar as Apple π. us.map(u => u.lastName);
Maybe that’s too close to the word “us”, meaning you and I. We can do better: u_s.map(u => u.lastName);
But then is that an abbreviation of something with the initials “U” and “S”? We can do better: users.map(u => u.lastName);
Point being that including the word on the line is what provides the clarity.----
And a bonus option for the truly maniacal:
you_guys.map(your => your.lastName);In a local scope, aliasing to single-letters is OK, and many codebases are littered with i,j,k,b,v,x,y,z arguments and iterators. But putting that into a nested scope has produced many errors for me as I accidentally declare a new n or v and then try to access the outer one. So I'd enforce uniqueness when nesting.
After some experience with Pascal, I thought the idea of implicitly reserving "result" as the name for the returned value was a good idea, but also a bit long. Taking a page from TI-BASIC I have settled on "ans" for "answer".
Global variables generally have to be a bit longer to not collide. In my own investigations, this happens at four characters: while you can devise some two and three-character abbreviations that work at language level, at four you start getting more complete English words(and English is a bit part of this whole estimation, more densely encoded languages will have their own metrics).
Types, functions and classes often cause woes, because they need to be accessed across modules, and modules will use names in linguistically dense ways, but they should not expose that as the default interface in most cases. Here I think the goal should be to have full namespace qualification as the default, and then gradual relaxation near the callsite through explicit aliasing and redeclaration.
That last part makes me think that I can go further with what I alias in some languages. Eg. in Haxe, I have the freedom to throw in "typedef Foo = com.corporation.big.fancy.Package.Classname.Method" wherever I like.
What people typically mean to say is something along the lines of "I haven't a clue what this does, haven't enough experience with the language to understand it, or how people use it".
Go look at code written in "safe" languages like Rust and tell me you understand what it's doing any better than well written C code.
You have to know the language to understand what is sane or not, what the conventions are, what the common issues are, etc.
Same with all the Functional stuff out there. To a OO person, it looks bat poo insane. But if you learn FP, it's natural.
Some of your complaints are valid from the perspective of someone who doesn't do kernel work. For the people who do work on the kernel, the macros, abbreviations, etc are common place and well understood, and a complete non-issue.
That kernel C code still looks awful to me.
I've seen beautiful code in every language including C.
“ the problem is one of cultural context which is often not shared between generations of coders. “
There is no such thing as absolutely good code. It’s context dependent and context tends to be ephemeral. The Linux kernel is in some ways an exception, but also appreciated by a pretty small group. The rant against the kernel code is meant to be illustrative - if you don’t know the conventions, abbreviations, if you’re not operating in the context of an early 21st century OS, where memory is handled manually, none of this looks particularly great.
Code is cultural and cultures are niche and ever changing. Particularly these days. The same technology that enabled the fast rise of, say, Ruby, or Go, or Rust makes your code and my code depreciate quite quickly.
> ...did I just see a data race...
You did see a data race.
That race is pretty clearly unavoidable, as it would be triggered by a sysadmin setting the value of a particular sysctl knob "low enough" in between the check mentioned in another function in the chain of function calls that this function is part of being run and the commented code being run.
(You can tell that this function part of a chain of function calls because that comment refers to a check in a function that is not called by the function that contains the comment.)
> ...okay because it is faster if it’s horrifically unsafe...
I mean, here's the call site of the "objectionable" function and the site at which the issue gets detected: <https://github.com/torvalds/linux/blob/master/fs/file.c#L174...>. At both ends, there's a comment that says "Hey, it's faster to do this check here, rather than over there.".
It's clear that the folks who worked on this code were concerned about the performance, and had discovered that relying on callers to discover if that race had resulted in them getting a smaller allocation than they wanted was needed.
it seems you didn't bother to read the entire comment, otherwise you would've seen I linked to TWO different programming languages.
Many people will say that an induction stove is better in safety, convenience and many more. But a gas stove still has its own place.
Just because the kernel code doesn't use the latest "best practices" doesn't mean it's bad. It could be due to many reasons from performance, compatibility, etc. Software is so easy to update anyway compared to other things that I'd bet there's more "good" software than "good" anything else.
Good writing is relative to the norms and expectations of the audience, which are other kernel developers in this case.
It’s written in an unsafe language.
So is every other mainstream kernel. Double underscores everywhere, which are the bad alternative to namespaces seen in weak languages like C.
It's the designated way to avoid symbol conflicts for system software. Cultural norms. Constants defined with lowercase and not actually marked as const.
There aren't any constants in fs/file.c though? Do you mean the sysctls? Those are modifiable at runtime. WTF does “BITBIT_NR(nr)” do!?
This is genuinely obscure without background knowledge. fs/file.c maintains a bitmap of bitmaps for performance optimization reasons, hence "bitbit". "nr" has been a standard abbreviation in this part of the kernel for decades. Abbreviations everywhere: ofdr, nfdt, fds, fs, etc…
Terse identifiers are just a cultural norm in kernel code. old file descriptor table, new file descriptor table, file descriptors, filesystem, etc... Then there is a long-winded explanation of how they pack bits into an array of longs. Okay, why not make this a reusable module of code? Because C is a weak language, or because the Linux kernel is spaghetti with a dozen implementations of bit maps?
Because this code is actually very tricky, performance sensitive, and basically unique in the kernel. The kernel has many things that could be de-duplicated, but I'm confident someone's tried refactoring this and failed for some reason or another, probably performance. Sorry… did I just see a data race just casually commented as “okay because it is faster if it’s horrifically unsafe?
You're seeing one of the many design tradeoffs that are made to get good performance in the real world. The VFS code this file is part of is one of the most performance-critical components in the kernel and gets involved with all the other filesystem operations that happen, which on a unix system is essentially everything. The code (and cache footprint) are smaller if the safety burden is pushed off to other people here, which is more important than maintaining an ideal interface.This is some of the most battle-tested code in the world. It's fine if you don't want to modify it, but it's solid code that people have literally bet their lives on given the mildly terrifying use of Linux in safety-critical systems.
But... his writing is archaic. Not just quaintly archaic, like a novel from the 1800s, but literally requiring translation archaic.
Worse still, the vast, vast majority of people "teaching" Shakespeare pronounce it wrong, which means most of the humour is lost: https://www.youtube.com/watch?v=YiblRSqhL04
The jokes don't work any more!
The rhymes sound wrong!
The whole thing is a farce. Theatre. We all pretend it is great English, whe in fact it isn't even English any more.
It's taught because our teachers were taught it. Those teachers... and on.
It's like the idiots still formatting cloud SSD virtual disks as-if they are physical RAID controllers with spinning rust.
It's like the Hungarian notation identifier style people copied from the NT kernel code, even though the NT kernel people realised it was a mistake and moved on.
The Linux kernel code would be crazy bad if it wasn't for tens of thousands of people beating on it until it has become merely mediocre.
It will never be perfect, it'll never even be "good" code. It has too much inertia, too much history for that to ever happen.
So no, it requires no translation.
I disagree completely that the humour is lost. There may be jokes I don't get, but that's more true for Futurama with all its 90s American telly references.
Perhaps Chaucer would be a better example?
> So no, it requires no translation.
It does for most students in most schools.
Sure, there's some grey-haired academics that insist it doesn't need translation -- but they've spent a career learning Shakespearean English!
> It's taught because our teachers were taught it. Those teachers... and on
That’s bullshit. Greeks are also taught, there are just eternal literary works.
Do you have a source on what I have managed to write or is this just guessing and calling it "fact?"
...
Every programmer occasionally, when nobody’s home, turns off the lights, pours a glass of scotch, puts on some light German electronica, and opens up a file on their computer. It’s a different file for every programmer. Sometimes they wrote it, sometimes they found it and knew they had to save it. They read over the lines, and weep at their beauty, then the tears turn bitter as they remember the rest of the files and the inevitable collapse of all that is good and true in the world.
This file is Good Code. It has sensible and consistent names for functions and variables. It’s concise. It doesn’t do anything obviously stupid. It has never had to live in the wild, or answer to a sales team. It does exactly one, mundane, specific thing, and it does it well. It was written by a single person, and never touched by another. It reads like poetry written by someone over thirty.
...
[ https://www.stilldrinking.org/programming-sucks ]nor is there such a thing as a good love letter
1) your code will be written once but read a thousand times
2) if you want to make your mark with style and convention start an OSS project of your own, otherwise make your mark through the simplicity and elegance of your logic, not the way your code contrasts from the code base.
good code solves the problem at hand. good code is as simple as possible. good code is testable and tested. good code is minimal. good code is performant. good code is maintainable. good code is understandable.
yes it matters somewhat that you have some consistency of the way you code. that's a secondary thing
But do you think there is bad code? If so, I'll give you the official definition of good code: code that isn't bad code.
https://dconf.org/2023/index.html#walterb
I've got the talk about half-written. I'm concentrating on things that are timeless about writing understandable code.
Although the examples will be in D, and will use D features, the concepts are transferable to many languages.
It will be livestreamed for those unable to attend.
More importantly: metaphors are not a healthy way to understand an idea. Ideas are more nuanced than a metaphor could possibly account for. Abstaining from metaphors might not make for a catchy headline though.
But "the map is not the territory"!
Example: Array of items as a shopping cart: how do you efficiently remove an item from the array (as you can from a shopping cart)?
You should expect some additional nuance when receiving a metaphor.
Thinking about code this way takes nothing away from appropriately deep explorations of nuance.
They coexist without issue.
Arguing the metaphor is pointless, because the metaphor isn’t the thing, it’s just a way to explain an aspect of the thing.
"Fast must be cutting corners and not just actually better at programming" is a weird fallacy to hang your hat on.
There's a huge cost to having engineers who are bad at understanding and solving problems. The cost of having people who are good at understanding and solving problems is supposed to be that you pay them more, but that's not how employment incentive structures work in practice because information asymmetry and stigma around talking about salaries.
hm, something about this that I just can't put my thumb on
1. https://en.wikipedia.org/wiki/ElitismThe code he produced was absolutely and completely unmaintainable. The joke was that this guy could program C in any language. His code kindof-sort-of worked, but only he understood it and when anyone else had to take over his stuff, the first thing they had to do was rewriting the part from scratch. Of course there were no tests, so when the new person broke something in their attempt to try to work with their code they were scolded. The person in question was the CTO of the company, by the way.
He was 3x alright, but at the expense of everyone else on the team. I was very glad to see him go.
Yeah..... pass.
You can have beautiful code and garbage product, and great code and a garbage product.
If your PM is pushing bad products, that's on them. Your job is to deliver good product, not good code.
I'm not saying that you should disregard your code, just that it's not what you should put at the top of your list.
You can build amazing products with amazing code, and they will often raise each other up.
A great example is Redis, that codebase taught me how to write C code, and I also use Redis everywhere.
Let’s be honest: sometimes you’re not “changing the world” like many founders like to think, just building yet another shopping cart.
If you lose love for your craft there’s no product that’ll cure it.
The code analogy here would be nerding out on how to use a hammer in spectacular ways, but not really do anything interesting with it.
You can still really love your tools, but you shouldn't focus on them as much as what you're building with them.
These two are not disjoint, but instead quite related.
> Your job is to deliver good product, not good code.
If your job is software engineering, then "good code" is what facilitates "good product." It boils down to maximizing the "abilities":
- Understandability
- Applicability
- Testability
- Maintainability
- Flexibility
- Durability
Better to have hacky code that delivers value than to deliver nothing.
"Hacky code" is most often a result of those responsible for delivery having gaps in their understanding of the product need.
When engineers have a deep understanding of what is needed, there is rarely a need for hacks. Of course, the obvious exception to this is when libraries and/or external services mandate them.
Who cares if it slows down feature development if you never have to change it. If it does impact the product, it then becomes part of the product value you're delivering. This is the time you should be investing in better code.
I try to keep that in mind when writing my code. Sometimes its comments, sometimes its documentation, sometimes its CleanCode™, sometimes its big blocks of text explaining why the heck a certain class exists, but my approach is "Write it for myself in a year when I can't remember what the hell half this stuff does", using every tool at my disposal to achieve that.
I don't really care that much about the developer, and care a lot more about the users of my product, especially since that developer is usually me.
If I'm unsure of the value I'm adding, or if the ability to do something is greater than its quality, I will take shortcuts if needed.
If the codepath is critical, or if doing so is dangerous, I will take my time on the quality and the foolproofness. I will be pertinacious in this mindset, and if managers try to rush me, I will remind them of the consequences of failure and log everything.
I've seen too many developers (myself included) spending weeks and months on perfecting something that's never used, or barely used. The latter issue of not spending enough time is a lot more rare, but does happen.
Future me will always appreciate how considerate present me was.
I like this term a lot more than "the next developer."
How can I help you without first helping myself?
That 'why' also helps to show that Past You knew wtf you were doing, and lets Present You feel confident in making changes, because you know what the intent was.
"I'll remember this!" is one of the greatest lies in CS.
I'm trying to think, but I can't come up with any kind of comment that has been useful that didn't have a "because" word, explicitly or implied.
Like, even if there's a comment like:
"This function only queries X and Y fields. Don't add more. If you need more than that, use this_other_function instead."
It has an implied reason behind it, and the mistake is not adding that reason to the comment. So this comment would be missing something like
"[...] because this is being used in a critical part of the code that is very nitpicky and it's very difficult to test because requires some annoying manual steps", or something like that.
There are probably situations where a "what" or "how" comment really are better than "self-documenting code" though. I would probably appreciate those comments if I ever need to read branchless code, SIMD code, or similar performance-sensitve witchcraft.
I've done this before in production code. One of my only exceptions to my policy against leaving in commented-out code has been things liking leaving the unoptimized serial code commented out at the top of a section of some hand-vectorized code full of SIMD intrinsic. It clearly showed the intended "what", but was also useful to keep around as a baseline for performance testing and reference code to compare against for debugging.
Seriously?!
It's simple. It's a matter of respect, and treating others as you would like to be treated. It's no fun trying to figure out the "why?" of some code you've inherited responsibility for. It can be frustrating and time consuming.
So why would you put others through it. Make the code and the reasoning behind it as clear to the next dev as possible. Explain it with comments. Refer to issue links. Make it so that there's minimal - preferably zero - unnecessary digging for the next poor soul who'll work on it.
Because I've found there's a bunch of developers who apparently think it is fun to figure out the "why?", and they code accordingly.
>So why would you put others through it.
Because they think their code is "self documenting" and perfect and that anyone that can't immediately understand their code is too dumb to be working with it.
>Explain it with comments.
We just had a discussion here in the last week or two where people were actually saying that comments are completely useless and should never be used.
>Make it so that there's minimal - preferably zero - unnecessary digging for the next poor soul who'll work on it.
Sounds great to me, but in my experience it's a minority of developers who agree with you. It makes working on others' codebases very frustrating in most cases.
I'm waiting for the day where AI/co-pilots can enforce team and industry best-practices, style, maintainability, testability, etc before the code is even committed.
Call it "uber-linting" and get rid of code reviews.
(The whole codebase is stripped bare of documentation.)
Copilot can write implementations based on comments. Soon I think it could flag potential errors if code doesn't do what the comment claims it does. Then we can do that in code checks.
When it misses then I have to read the ** code to use the ** thing. And I am not interesed in that at all. Maybe they do this so someone has to read their "good" code.
Often this is not true. Our creations are judged by the user. They don't care how good it is under the hood. They care that it works correctly, that it's easy to use and that it's as fast as they need it to be.
Your boss should care that it's well written because that should mean it's cheaper to maintain. But, they don't look that far into the future when evaluating your performance. So, often they only care about how fast you did it and how happy the customer is with it.
Our industries incentives don't often align with good code.
Years later when I desperately needed that rack to keep the site up I was able to roll it in and light it up without waiting 6weeks for an electrician change request.
He saved my bacon by thinking ahead. It was a gift he gave me, never mentioned and was not around to receive thanks for.
I aspire to be more like him in everything I do.
1. Don't use fancy design patterns, FP constructs, performance optimizations etc. Unless the use case specifically requires it.
2. Code should do whatever it needs to do to fulfill its business requirements. Nothing less and preferably also nothing more.
3. Readable code beats documentation: only document the 'why', and only when it is non obvious. If you find you're documenting the 'how', have a second look if you can write your code in such a way that it explains itself.
4. Explain how to build the project, run tests and whatever else is non obvious in a Readme file in your project. Documentation anywhere else, such as on Confluence, will not be maintained or read.
I think the code should be "optimized" for the expected audience (of other devs). If you're a single rock star in a mediocre dev shop, you need to code differently than when you're part of a team of MIT PhDs.
It's harder to read the code than to write it so if you can barely comprehend the thing you just wrote, you probably won't understand it in the future: https://sonnet.io/posts/code-sober-debug-drunk/
2) I think it's more like speaking with ghosts (including a spoiler for The Sixth Sense):
Isn’t this only true of bad code? Good code is almost certainly the other way round, harder to write than read… just like a really good book was much harder for the author than it was for you, the reader.
The quote you call into question is semi-famous and I believe the idea is that it's relatively difficult to jump into one code unit of a larger system and get back up-to-speed because one must fill the data cache of their brain with all the considerations for why the author may have done things in a certain way. The algorithm choices and edge cases and exceptional logic paths were clearer to the one writing the original at the time.
> just like a really good book was much harder for the author than it was for you, the reader.
Now, I suppose this could work for a nested narrative novel, where the reader is meant not to just read the book, but to rewrite and expand it as they're reading it. Adding new ideas, new sub stories on the way. Think 100 Years of Solitude blended with Italo Calvino's Invisible Cities.
Seems like the [*]Bible is a good literary example of an async collaboration similar to code: notoriously hard to interpret, and full of dubious historical information and contradictions.
I know that Christians call it the "good book", but most of them don't use the word "good" in the same way they'd use it to describe a Nabokov or Eco novel.
[*] or most ancient religious texts, like Avesta
Controversial opinion, but in my experience so far people usually say "this is best practice" as a way to avoid thinking, or to masquerade personal preferences. Not always, but more often than not.
As soon as you ask "why is it a best practice?", "what are the consequences of not following this best practice?", "since 'best' implies there's a 'not-best', what is an example of the 'not-best practice' that this wants us to avoid doing?".
The answer is usually something vague and handwavy like "because it's more scalable", but if you challenge that by saying "what do you mean with 'scalable'? can you give an example?", you usually won't get an answer.
Doing stuff without understanding the reason, that's cargo cult.
To be clear, I have nothing against people saying "I understand the code better like this"; it's subjective but honest, and it doesn't try to pass a subjective argument as if it were an objective truth. What I dislike is people saying "this is best practice" like an absolute truth that must be blindly followed and never challenged, without even considering the circumstances of the current project.
Please spare me from turning a simple HTTP CRUD of 4 endpoints into an overkill Icosahedronal Architecture with a reason like "when we need to write an v2 this will make it easier without needing a rewrite!"; because I know that when the time comes for an v2 you'll rewrite from scratch anyway because then you'll want to use the new and better(tm) Perpendicular Riemann Zeta Architecture.
There is almost always pushback on security considerations, and partly because those methods quite often change and improve with time.
The term then becomes a blanket phrase that captures the constantly evolving landscape and necessity of accepting those new changes.
You’re right that it is becoming over used. Like most abridged language, once your favorite becomes popular a new way to express the same underlying idea usually comes along.
If a nobody like me writes a random blog post saying my custom way of doing ThisSecuritySensitiveThing is the best, at the next microsecond I'll have half the world linking me to like 20 different papers and 5 real incidents proving why I'm wrong.
And I wouldn't be able to wiggle my way out of that criticism with responses like "but it works for us".
I realized that when I did that project I "had the time to care" which is not the case lately!
Just today in fact I was trying to update some code I wrote two years ago because one of the underlying tools broke. I was very happy with past me for commenting the workflow of that tool so I could easily work around it.
What you're supposed to do is write the code as clearly as possible and then add comments for anything you couldn't manage to express in the code. Usually that'd be all the context around why the code is the way it is and isn't the way it isn't.
Now this is something I could never do. Or find in other people's code. I much rather appreciate the good comment explaining to me WTF is going on, annotating the larger segments, and so on. Of course it might just be my personal limitation as a programmer that's far from the best in the craft. But to me, comments are the most time tested.
There's lots of important information that comments can convey which code itself cannot. In particular, a program's code can tell you how it works but not why it was designed to work that way.
And after a point, even trying to convey too much information about how a program works through code can be cumbersome. We've all seen function names that are way too long, because the author wanted to cram way too much information into it. That extra information should have been put into a comment, where the author could have articulated it clearly, instead of as a single overlong compound verb in camel case.
Some Leetcode jock will come in one day and rewrite it anyway, or a new CTO will crash the joint holding a bathroom sink and make your code an orphan. Or the company will just disappear.
The code that i write like my own is, well, my own. Because that’s the code I come back to years later and I maintain, and always will. Everything else is ephemeral.
The times you appreciate it are actually when you open something and it’s nice and easy to change and you realise you were the one who wrote it. But real developer happiness comes when you have the same experience and someone else wrote it.
I recently worked on a project with some seriously clean code. My job? Shut it down. This made me sad.
This applies to artistic endevours like books and paintings as well (especially those two, since markets for them are very oversaturated). Your technique might be masterful, but if your art doesn't align with current trends (along with a number of other factors), it'll drown in the endless barrage of other art, and nobody will stumble upon it or pick it up, even if it's still available.
Sometimes the code is beautifully factored and tested. Maybe even documented. It then proceeds to fight against the new change. Maybe the type invariants fail everywhere for reasons that price spurious. Maybe code far away makes dubious assumptions and breaks in response. In the worst case, it's beautiful nonsense held up by undefined behaviour and the language has come to collect the tax.
I like code that can be changed to do new stuff without everything around it exploding. That's probably what I'd call good code. It's in the context of tolerating future requirements well as the future is now.
I do not see such code often.
This is fairly tired conversation and while superficially interesting, there is simply no way to define good code except through local consensus of the people writing and reading the code. But that's as far as it will go, and time will will take what's owed to it.
It turns out this is true for well over 90% of relevant topics on the planet. Every year in the US, over 4 million people turn 18 years old and over 3 million people of various ages die. If we contract this dynamic to people who enter and leave our industry, it becomes clearer why it is not only helpful to say what has been said already, it is vital.
The above has already been said also.
Writing larger test cases that test the actual functionality is a game changer. With Testcontainers and good mocking tools you can spin up a real version of your app with external dependencies mocked and faked, and write test cases against the same input layer your user uses. If your test fails you can be pretty sure it’s because you broke something.
This is a layer between unit and integration tests that you don’t read much about. I call them “service tests” (coming from a microservices world).
Investment is still needed though to reduce the effort of implementing tests, like extracting common fixture code so that every test doesn’t need long winded setup code. Also snapshot testing libraries take the pain out of writing assertions.
I am speaking from the API based backend world so YMMV. Once you’ve worked this way it’s hard to go back. Any developer can come in and confidently make changes. You spend way less time investigating regressions and broken environments, so you can release more frequently (which takes a whole other layer of pressure off the team)
Personally I came to terms with myself for not having to do that. Some companies who rush things to market do not understand it but it is okay with me.
Looking back at years and shipped projects I do not trust such metaphors as in the article.
I prefer other pov. Where I grew up one songwriter and performer coined it (sorry at incorrect translation) "[they] write as they breathe. It is a natural order of things". So if I do not like working with someone's code I have to move on.
Like, 1000x more.
Code works. Not all work is fun and beautiful, some work is not even worth doing. But making something work because it needs to work is also worth doing.
Years ago a wise man told me "The compiler is not your customer. The next person who maintains this code is your customer. It might be you."
If they have to look at your code to figure out how to use it, you've already lost. Hence the code isn't the product, it's the means.
Who wrote this crap?!
`git blame`
D'oh!
Just today I found an old spec I wrote that was so completely mocked that the code it specified made no difference. I wasn't feeling the love.Just reading isn't as good as interacting with it
Good code is contextual and subjective.
I call BS on this one. TDD is usually a sign of a struggling junior and results in the worst code quality possible.
Also, the next developer is in many cases myself, months later, trying to fix something or adding a new feature. I wouldn't care about other developers, because everyone has their own style and niggles, it is enough to cater to my own niggles, can't be bothered about others. And I also don't make a fuss about code written by others, as long as it meets some minimal standards.
then I have been in some bad relationships.
"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."