61 comments

[ 3.2 ms ] story [ 133 ms ] thread
I don’t see the need for stupid in all caps.
It's an acronym. Each letter stands for a coding principle that I follow (and encourage others to follow) to write maintainable code. :)
It comes off as click bait.

Personally I have no issue just figured I would relay my impulsive "news aggregation junkie" thoughts.

"STUPID: A mnemonic for great code" might be less impulse triggering and be more forthright. But you have to bring them in somehow so meh.

Before these two comments, I would have assumed most people know before clicking that it's an acronym that derives humor from the contrast between "smart" and "stupid". Maybe periods would help?
FWIW: My hunch when I read the title was that it would be about “overly smart” programmers who tended to write code that was too dense/tricky to be maintainable by anyone but themselves. Hence smart programmers writing “stupid” (aka unmaintainable/inscrutable) code.
Maybe. I don't feel strange for assigning the precedence of a word over an acronym at 6 letters when the context comes into play.
Ubiquitous made little sense to me. Proper works much better in French I presume. Not much in the way of clear justification or examples, just a list of things.

Altogether, some OK ideas, but quite generic and insufficient/incomplete. Coupled with the conversational marketing style, on the whole, frustrating.

Keep thinking and writing.

> A compiler will not call you in the middle of the night to tell you how amazing your code is. A co-worker, however, might call you in the middle of the night during his on-call night shift to ask you why you introduced an object X that has the same meaning as object Y

What a treasure of an article. Definitely felt the leveling up happening as I was reading.

I don't understand what that passage is supposed to mean. Yes, you write code to please the compiler. Moreover, you first look for ways to make the compiler harder to please, rather than just use its defaults.

You don't write code just to please the compiler, but it's a big deal. (Obviously, if you just wanted to please the compiler, it wouldn't have to be indented, and your variables could have names like l0327.)

A sufficiently smart compiler could warn you that you're introducing an object X with the same meaning as object Y, so your coworker can sleep at night. Or if not compiler than some other tool. We can imagine tooling that scans the code base and tells you that a class exists that is very similar to what you just wrote.

A compiler was written by your de facto coworkers, likely in another organization far away. When their ball-busting attempts turn up nothing against your code, that is a kind of praise.

It seems like the U should be “Unambiguous” rather than “Ubiquitous”?
It's referring to the "ubiquitous language" concept popularized by the Domain driven development (DDD) literature. The assumption is that in every business there is an existing language, i.e. a set of terms that refer to the business concepts and are used by all employees when taking about goods, processes and so on.

Developers should take care to elicit this language from their colleagues and use it in their code instead of using whatever terms come to their mind when they need to name things.

> about debugging our system at work that’s coupled to a SAP service in Wisconsin

I think article is a bit naive. If your system is designed this way, you probably have no control over it. Maybe some legacy stuff, maybe you jump too fast between 300 microservices...

It hat case you need other methods, like very good logging. If you can replay every interaction with that service, you do not have to decouple it!

Unfortunately, if this becomes popular, I suspect it'll merely turn into another useless dogma like SOLID and OOP and whatever other buzzwordy acronym-paradigms came before. I can make my own acronym with the same letters too: Simple Teachable Universal Powerful Intuitive Direct. Write some fluffy marketing-esque material with vague references about how each of those terms is a good thing, and with luck, you'll amass worshippers and develop consulting services and other sellable material about how much better you can make software by following these simple rules.

Does all this "snake-oil" actually work? My decades of personal experience says otherwise. You can't teach surface-level rote memorisation crap like this and expect it to make things better. I agree that simplicity is good and accidental complexity is to be avoided, but it's not something that can be distilled into trite talking points to be taught.

"There's no replacement for intelligence."

The problem isn't intelligence, the problem is judgement. Many a smart person has built ridiculous and rickety towers of code, only understandable because they are smart.

Good judgement mostly seems to come from bad judgement, failure, then picking the pieces up again while evaluating the causes. It also seems to require some emotional maturity and humility.

You are correct that the various dogmas are not useful until the mental attitude is right. After then they are superfluous.

(comment deleted)
I like to think I tend to write code that others like because I am usually not the smartest person in the room. I strive to make my code simpler because I can’t understand less simple code. If I find an approach to solving a problem that feels too complicated to me, I usually keep looking until I find something simpler.
I would argue that this is not related to intelligence, but wisdom. Wisdom is something you earn through experience and can't be taught.
Yes.

Un-wise super intelligent programmers are the worst. They come up with the most amazing roundabout way to achieve even the most mundane tasks, and consider complex code a mark of honour, rather than a pathology to be purged.

At some point people mature and realize that their job when writing code for work is not to concoct brilliant puzzles for next maintainer, but to do exactly the opposite. And then they reach the meta-level of realizing that simplifying things is actually just as hard and pleasing as puzzle-making, and they've reached a maturity in the quality their program design.

The worst kinds of programmers are those that plateau at the "Riddler" stage. They potentially are able carve themselves a nice safe niche, but the value they could have delivered could have been so much more if they'd ever matured. "Oh, that code... yeah, better call Edward ... it's his baby, supercomplex. Nobody seems to be smart enough to work in it except Mr. Nygma".

There are pieces of code that are potentially super-complex due to the intrinsic complexity of the problem space. But since for an outsider it's impossible to distinguish between the two, utmost empathy should be always used to introduce new people to such a thing.

Wisdom can be written down and read. Those who seek may find it. Philosophy means The Love Of Wisdom.
Someone seeking wisdom is different than someone stumbling across wisdom before they are ready.
I remember after learning OOP I found myself unable to write any code without violating some principle.

It's like trying to solve a puzzle that doesn't have a solution.

> simplicity is good and accidental complexity is to be avoided, but it's not something that can be distilled into trite talking points to be taught.

No, "simplicity is good and accidental complexity is to be avoided" is that distillation. The difficulty is less the "what" than the "how". Compressing experience, expertise, wisdom, taste, humility, judgement and courage into an acronym is always going to fall short. Another useless dogma as you say.

I don’t know that every acronym is necessarily bad.

DRY, KISS, SOLID etc.

Yeah they’re buzzwords, but they serve a pretty useful function of constantly reinforcing objectively good behaviours that should be inherently obvious but often are not.

That said, my argument falls apart with DevOps which is simply moving automation to more stages of your delivery (which is good) because it’s been utterly co-opted by snake oil salesmen and uppity gatekeepers. Seriously, the amount of exceedingly difficult but otherwise passable workers I’ve had to straighten out because they’ve mistaken having access control rights to work with the production environment with being in charge of the whole development process is utterly wild.

(I’ve checked too, no-one has told them or lead them to believe this so I’m sure it’s A DevOps thing).

And, you know, Agile. But it’s not an acronym so I’ll leave that rant out.

I have seen so many codebases turn into indirected, overabstracted brambles as a result of DRY. I’ve seen it so many times that I would not call DRY objectively good.
> Unfortunately, if this becomes popular, I suspect it'll merely turn into another useless dogma like SOLID and OOP and whatever other buzzwordy acronym-paradigms came before.

Yes, but that said, if there’s gonna be a dogma I think this one is pretty good. To me, software engineering is the art of curbing complexity.

I can see a couple of missteps:

- simplicity can be confused with the appearance of simplicity. Yaml is a great example of this phenomenon.

- simplicity is subjective, and should ideally be measured by outsiders. However, as we all know decisions and moat belong mostly to insiders, senior engineers that often want to flex and build moat. There’s a risk of curse of knowledge because you can just say “this is simple to me (and if you don’t get it you’re stupid)”

- when more people can understand something, you can get more chefs in the kitchen. This is not a problem with simplicity but can be a problem of governance.

- engineers can move visible complexity around to areas of less scrutiny. For instance, if there’s a “rule” that a module should be simple, you can break it into two and say they are each simple (even if they’re coupled and it’s only a cosmetic modularization)

- complexity from dependencies, concurrency, memory usage etc can fly under the radar, especially for less experienced/gung ho engineers.

To be clear, I still like this new trend, these are just some immediate issues that come to mind.

Parts of it already are dogma. The “testable” and “decoupled” sections come to mind: collectively we’ve taken these to extremes. I have seen so many pointless (and more importantly at large scale, inefficient) abstractions to satisfy arbitrary testability and decoupling requirements where I work. I work at a place that isn’t quite FAANG, but it’s operating at huge scale nonetheless. I’m tired of seeing one-interface-one-impl everywhere, sad attempts at mocking dependencies, and so on.
Smart programmers don't need any of this shit.
Have you ever worked in larger team where everyone was a brilliant developer? If you have, you were the dumb one.
I would think the U should be "understandable"?
It would have been much clearer. Ironic, isn't it?
Decoupled code is much harder to follow. I see people thinking decoupling is always good, but it's not. It can be good when things are actually de-coupled... but I've seen coworkers decouple logic from HTTP handlers for example where that made no sense because the whole purpose of the logic was to do stuff for HTTP... so if you want to ensure that if certain conditions are met, a HTTP header has a certain value... well you can't anymore unless you abstract away even the concept of HTTP headers! The whole thing becomes a mess out of the good intentions of the developer. Do not ever decouple things that are intrinsically coupled.
Most people decouple code for the sake of decoupling (cargo cult) vs. actually knowing when to do so. For example, I was reviewing a software project written by a bunch of junior/mid-level devs. They had decoupled the clock from the physical clock (good for unit testing time-based workflows), but when asked about why they did it, they didn't know or understand why it was done and there were no unit tests using it. When I asked the dev who implemented it, he explained that he decoupled it and so it was good. (to be clear, I was only seeking to understand, and not asking these questions in an accusatory way.)

There also existed many interfaces with exactly one implementation, which just increases the cognitive load when writing code because you see an 'IDoX' interface only to discover it is ALWAYS an 'XAdapater' that if you had known, would have saved mountains of logic (ie, getting a new 'XAdapater' from the container while copying some stuff from the 'IDoX' interface). It was a mess.

Are they going to be glad that all these abstractions exist at some point? Probably not. This is a pretty simple, but mature project. There's no real reason to refactor it at this point. The business owners just wanted to know what was going on and why simple changes took months to implement.

Frankly I think even decoupling for the purposes of unit testing is another example of cargo culting. It emerged as a best practice when CPUs were much slower, containerization didn't exist and mock service tooling was far inferior - writing integration tests was an exercise in flaky futility in 2001. The technology has since moved on but the culture of best practices hasn't.

It still can make sense if you've got a very complex bundle of isolated stateless business logic but in practice I find that most dependency inversion I see these days isn't for that. It increases the SLOC by 30% and reduces code coherence just so that a test can run in 20 milliseconds instead of 2.1 seconds.

In many cases those unit tests check to see if you put a number in one end of a class that the same number comes out the other end. What bug is that going to catch?

Meanwhile all of the actual bugs are probably hiding in the database queries or the interaction layers created as a sacrifice to bob the bearded god of unit testing.

The most fascinating instantiation of this idea is, I think, the notion that because unit tests are horrible to work with that unit tests can therefore "drive" design because it'll make you fight to make the unit test less horrible. It's like advocating an abusive relationship to help fix your life because the abuser won't be afraid to point out all of your flaws which you can then fix. The two corollaries that never seems to permeate are A) maybe when unit tests become horrible to work it's means they were also the bad code that coupled too tightly? and B) maybe it's cheaper to spot bad design by learning what to look for rather than building a unit test that will punch you in the face for it?

Clock decoupling is one of the few patterns that's actually required for reproducible tests. Using the real clock makes test run in realtime (slow) and puts them at the mercy of the OS timing and other exterior factors.
Not necessarily. This is exactly the kind of thing I mean, in fact.

I once had a bug scenario I wanted to reproduce with a test where the time was grabbed from datetime.now() in the first half of the scenario and indirectly via a postgres query in the second half (with a time delimited query).

With libfaketime I injected a new running service time and postgres service via the test runner. It let me do TDD to create a reproducible test - what you thought was impossible.

I could have jumped straight to dependency injection on that mess of a code base but that would have meant a risky architectural change that I would have to have done without tests. That is bad practice.

This sort of thing was not really possible in 2001 but it's possible now.

> It increases the SLOC by 30% and reduces code coherence just so that a test can run in 20 milliseconds instead of 2.1 seconds.

There are people who cargo cult and people who do things for a reason. It may not always be obvious which category a developer was in just by talking to them or looking at their work.

Imagine the following scenario: you have two projects each having the same functionality and the same level of test coverage. Project A is 30% longer than project B but project A's test suite runs in 1 second while project B's runs in 2 minutes. Personally, I am taking project A every time (but I'm also probably trying to figure out how to get that 30% down to 0.1-2%).

The path to project B is paved with 50 decisions to do the thing that took 2.1 seconds rather than 20ms.

>Imagine the following scenario: you have two projects each having the same functionality and the same level of test coverage. Project A is 30% longer than project B but project A's test suite runs in 1 second while project B's runs in 2 minutes. Personally, I am taking project A every time.

Project A's tests will usually be sacrificing realism in favor of speed. The practical upshot of that is entire classes of bug won't get caught.

To take some recent practical examples off the top of my head: I have some tests that do 404 checks on all web pages. I have another that checks if sent emails are outlook compliant. These checks necessitate that the tests be slower but somebody still needs to do this.

I know there are plenty of people out there who think like you do. That was kind of my point. Realism in testing is an underappreciated quality.

Encountered this too often in my career and had a lot of wtf moments debugging that «de-coupled» stuff others (and sometimes me) have left.

I decided to follow the «Wrong abstraction is worse than duplication» approach. Doing this for 5+ years now and did never regret it.

Sure, copy-pasta sucks but in comparison to over engineering it’s an acceptable price to pay.

The biggest pitfall is DRY imho. Write generic code, reuse abstractions. It's the most elegant way. Except it always tends to lead to these god classes, super systems, that try to be most flexible and to do everything. Then comes the point where it becomes impossible to understand all the interactions and half of your codebase falls apart if anything changes. All because you were to proud to copy & paste a few lines like it's a deadly sin.
DRY also removes an easy opportunity to expose patterns which are things that people are incredibly good at recognising and internalising. I used to religiously apply DRY but now I prefer to leave repetition in unless the git log shows changes being applied to the repeated sections.
To my mind, all discussions about code design boil down to the reduction of cognitive load (CL).

“Cognitive load refers to the amount of effort that is exerted or required while reasoning and thinking. Any mental process, from memory to perception to language, creates a cognitive load because it requires energy and effort. When cognitive load is high, thought processes are potentially interfered with. To the UX designer, a common goal when designing interfaces would be to keep users’ cognitive load to a minimum.”

(https://speakerdeck.com/fedepaol/reducing-cognitive-load-yet...)

In this context, "users" can be substituted with "other developers". Writing low-CL code is a constant balancing act and, like good UX design, when it works the nuances are invisible and therefore hard to learn from.

So I appreciate the OP for the article even if we can quibble about some of the points.

There is no one metric of code quality. There are a bunch of competing concerns, some of which will push others down if you push them up.

With cognitive load it doesn't even generalize. Who hasn't worked with somebody who joined a project and then immediately started changing the code to be more aligned to their taste in idioms to reduce the cognitive load. Their cognitive load that is. Not necessarily anybody else's.

First day of a new job, a new commute all have exceedingly high cognitive loads. Come back from that, all you did was just HR stuff and a new commute and are exhausted! After a year, suddenly that commute, the byzantine policy stuff has a cognitive load of, "yeah, this is all super simple."

My point, the unfamiliar might have actually lower cognitive load. Cognitive load is a function of what you know and what you are used to.

Great another one, throw it on the heap with

- SOLID

- KISS

- YAGNI

- DRY

- ...

- FUBAR (Functions Uncomplicated, Bringing Algorithmic Righteousness)
XP, SCRUM...too many managers for so little management work
I'd prefer BORING instead of STUPID. I don't want to see coworkers talking about writing "stupid code" all-day.
Yes! This! The code should be so boring it's never a point of discussion. This implies several things - for example when bringing in a new person, it should be straightforward to teach them how to work with codebase.

Other implications include naming conventions, sufficient amount of documentation, what architectural patterns to choose (if you need to do something that even hints of accidental complexity you need to have really good justification for it)...etc.

> The code should be so boring it's never a point of discussion. This implies several things - for example when bringing in a new person, it should be straightforward to teach them how to work with codebase.

Go is your friend.

Go is your friend with whom you can talk about the weather and sports but the relationship is ultimately superficial and unsatisfying.
But neither is it an impediment to maintaining your friendship.
Go is also the person who shows up when you need an extra hand for a home improvement project.
Go is a sort of pay-it-forward scheme, wherein you let go of your self-absorption and give the next guy a lucky break. Pun unintended.
(comment deleted)
I don't know if I necessarily agree, especially with D = decoupling. However I do like the idea of dead simple code. Often I'll find code written by self styled haxors who omit curly braces and nest ternary operators because they can. That's great but it ends up biting you in the ass.

The code itself should be treated internally like part of the product and it should be easily extensible, of uniform style, and written so safely that if a child added a line to the code it wouldn't break it entirely.

This is especially true of languages that support macros. If inclusion or modification of macros in your code cannot be done, it is probably a bad sign. When writing code to be resistant to hardware attacks for instance there is a different style that must be adopted entirely, if everyone is writing the fanciest for-loops fathomable then it becomes inefficient and a risk to code correctness to mutate the code to resist classes of attacks. This is terrible and not worth it just because an elite haxor wanted to write a for loop in a single line.

> Often I'll find code written by self styled haxors who omit curly braces and nest ternary operators because they can. That's great but it ends up biting you in the ass.

I've worked on codebases where omitting curly braces for single line IF was the style, but can't recall ever being 'bitten in the ass' by that particular style choice. I can see how when combined with other questionable style choices it could yield some very ugly and error prone code, but by itself I really have trouble understanding why people object to it.