Write dumb code
matthewrocklin.com
matthewrocklin.com
In other words, if you only ever use the very simplest features of the language, you are effectively reducing micro complexity but increasing macro complexity. One of the best examples of this is the classic arguments over the use of the ternary/conditional (?:) operator --- those against say it's hard to read and should always be replaced with if/else, while those for say it simplifies the code by removing redundancy and should always be used in place of an if/else with a single assignment statement with the same LHS in both clauses.
Similarly, if you believe programmers are almost completely incapable of understanding operator precedence, or that it "makes code hard to read" and restrict expressions to only one operator (and mandate that variables be created for all intermediate results), then you will get code with all the verbosity of Asm but none of the benefits.
In other words, good code may not be immediately obvious; but your reaction when you do understand it should be one of pleasant enlightenment, and not "I can think of a simpler way to do this."
A somewhat related article: http://typicalprogrammer.com/what-does-code-readability-mean
It's getting ridiculous at times. I've seen cases when people complained about "too complex" features in a language, where it would take them literally two hours to acquire necessary understanding, and become more productive forever. But no, it's easier to keep wasting much more time complaining. It smells of extreme anti-intellectualism.
Code is meant to be read, yes, but to be read by educated people. Programming is a profession, and it's unreasonable to artificially handicap everyone in a company just because some people can't be bothered to spend even just a little time learning if they don't understand something.
Totally agree, and I think this is what a lot of the "keep code simple" articles miss. To create something simple, you almost always have to create something complex first. All the best designs I've seen created were the result of 10+ iterations and several major refactors.
For a newbie in a number of subjects many things may seem complex but are actually quite simple once you study enough of the concepts or theory behind something.
Sometimes to see the beauty of simple there's not other option then learn more. And I'm against of trying to avoid people to learn something that is worth it but may be new just to keep the status quo of any anybody should be able to understand it even without any knowledge of things making it more complex instead.
Unless I'm remembering incorrectly I'd say the opposite, vb6 had a pretty sane feature set comparable to "modern" languages like go and rust. Coding with that feature set will give you simple and understandable code, if a little verbose at times.
The spaghetti code it's famous for was mostly a result of the event driven, drag and drop nature of the IDE and community. If you want real spaghetti code then try vb.net which suffers from the same problem but allows devs to pile on a bunch of OO features that obscure what code is doing.
What's not sane about Go's feature set? (Except for its debatable omissions.)
Usually dumb code is layers upon layers of needless complexity, grown because the coder had neither time nor inclination to understand the problem, and kept tweaking.
Such dumb code becomes a huge support and troubleshooting cost, letting alone quickly modifying it.
Don't write dumb code. Write simple code. The recipe is known: think about the data first, untangle and minimize dependencies (including any global state), write simple, orthogonal functions over these data. Use the language's standard library and built-in data structures. Crave for these negative line count PRs.
It takes much shorter time than working with dumb code.
Ergo, dumb code. It's hard to make dumb code that actually stays dumb instead of turning into genius-level complexity or bad abstraction.
There ate actual real reasons you shouldn't or cannot write dumb code. One piece of such "cruft" is handling numerical corner cases which is unavoidable unless proven mathematically to be impossible (And commented why). Using IEEE floating point? Guess you get to handle ULP precision, NaN and Inf. Using integers? Welcome to wraparound. Memory management? What happens if an allocation fails? Threads? Race freedom is quite interesting to achieve.
Using Java? Then figure out what happens when you choke GC with hundreds of objects. Etc.
You can only go so far in passing the buck to library authors, they can make mistakes too.
Straightforward is the way to go.
One of my favorite quotes :-)
Omit needless words.
Write in a way that comes easily and naturally to you, using words and phrases that come readily to hand.
It is always a good idea to reread your writing later and ruthlessly delete the excess.
Avoid the elaborate, the pretentious, the coy, and the cute. Do not be tempted by a twenty-dollar word when there is a ten-center handy, ready and able.
That's pretty much what I did at the start of my career. Nowadays I think things through a lot more (and generally come up with a lot more robust solutions).
Although do bear in mind that some professional linguists (Geoff Pullum being the ur-example) consider EoS to be an abomination of ignorant claptrap.
Amazingly well put. I’m gonna try and not forget this so I quote it when discussing with people who love to come up with bloated, overengineered solutions.
So I feel like the author of this essay is talking about some costs (the reader's) without considering the cost of the writer. If the code is never read because other solutions are considered more viable, then all that cost in making it simple for not. As always:
1. make it work.
2. make it nice (if it works and is going to survive to code review).
3. make it fast (if it has performance problems).
Unless you are completely on waterfall, you aren't going to be able to do 1. and 2. concurrently.
It boggles my mind when people who are just building an everyday website decide that querying models from the DB means we need a multi-level class hierarchy that requires a 5 page paper to explain. Or that anything remotely related to math means we need to start overloading operators. Congratulations! Your brain recognizes patterns! Wow, someone should make you the President of all Engineers Everywhere.
The code you're writing now will likely not survive another 2 years, especially if every time there's a bug or even a question like "how does feature X work under condition ABC?", everyone shrugs their shoulders and has to read 1000 lines of horribly nested abstractions.
If I can think of it, then I hope it already exists and I can just reuse it. The tricky stuff that needs a lot of thought is the same stuff that you can't just reuse off the shelf.
> It boggles my mind when people who are just building an everyday website
If it is something so routine that it doesn't need to really be designed, then I guess you can do it right the first time from memory (shame you just can't reuse some functionality if it is so routine though).
> The code you're writing now will likely not survive another 2 years
I work in research where your code is lucky to survive 2 months. I don't disagree with you, but you can't crank out perfectly simple code on the first go if the design space is clear. Those abstractions usually don't exist because the programmer was trying to be clever, they often exist because the programmer was trying to solve the problem. Its only with hindsight that we can often see a better more simple way (and hopefully have time to go back and do the simpler way).
There is also a cognitive load of 'using it' for simple algos and packages, like left-pad. Some end up the de facto standard of a community (Python's `requests` package), but other communities fracture (Node's SuperAgent, Axios, got, Request, reqwest, ...).
I like to think of code I write today as automatically being 'obsolete' 12 months from now. I might not replace it +12 mo, but if I _were_ to replace it, what comments, patterns, and hints might I leave my future-self to make that easier? Simpler code is easier to test and replace. Less magic code is easier to swap out with other languages. Verbose code is easier for my new team of +N engineers to digest, even if it takes twice as long to read.
I guess the rule of thumb is to write code to most clearly express the intent of it. Often harder said than done but a good goal to strive for.
Deleting code doesn't necessarily mean you're making it simpler. Compressing a 5 line function into a snazzy 1-liner is almost certainly making the code worse, not better.
> Every line that you write costs people time.
This kind of mantra is how you end up with nested ternary expressions and tightly wound functional compositions in imperative languages.
What about using something like gofmt? That way you can't get rid of lines by just rearranging the code.
- https://en.wikipedia.org/wiki/Rule_of_least_power
- https://www.w3.org/2001/tag/doc/leastPower.html
- https://blog.codinghorror.com/the-principle-of-least-power/
I also would like to add this blog post by Li Haoyi (from Scala.js fame)
- http://www.lihaoyi.com/post/StrategicScalaStylePrincipleofLe...
I think the tradeoff is maintainability and flexibility. I think code cannot be allowed to be complex. Complex code will eventually be broken during maintenance.
Also you have to be careful with using "interns" as a measure of minimum code level. Often the interns opt for what they feel is simple to understand but can be prone to errors. There are many micro patterns that avoid many problems that come from experience. But the sentiment about not using too clever code is generally good.
I fully disagree. Code is a liability taken on as a calculated risk to perform some task.
Code is an asset, like any other piece of capital. Like physical capital, using it and maintaining it may have costs. Like physical capital, the costs associated with it are not worth paying if you don't have a productive application for the capital asset that justifies those costs (and, also, the amortized cost of acquiring the asset in the first place.)
Recently I removed three custom written classes - one involving a friendly wrapper around .Net Http client, one to handle writing and reading CSV files and one to handle retry logic. I found better implementations on Nuget and replaced my custom versions.
Why? Because I knew my future self would appreciate not having to maintain them. Of course I had unit tests around the existing functionality first to help keep me from breaking stuff.
Any company has a narrow niche for which it is writing software. Avoid writing anything outside of that niche and use third party software. You are not giving your company any competitive advantage by writing yet another wrapper around database access.
When you possess 200 units of some currency, that's strictly better than possessing only 100 units. However, 200 LOC are not strictly better than 100 LOC.
When I replace a 1000-LOC program by a shell one-liner that does the same thing by composing some coreutils, I have removed 999 LOC without losing any value.
The asset is not the code itself, the asset is the value that you derive from it. Given a certain value that you want, the best code is that which brings this value while being the most maintainable. The article argues that the simplest code is usually the most maintainable one, and thus to be preferred.
Yes, which is why I didn't say lines of code is a currency (or even a commodity), only that code is an asset. Having 200 industrial robots comprising one factory isn't strictly better than 100 different industrial robots comprising a different factory — industrial robots aren't a currency. But factories, and the industrial robots within them, are still textbook capital assets.
> The asset is not the code itself, the asset is the value that you derive from it
No, the asset is the code. As with any asset, the reason you acquire the asset is the expectation of a deriving a value from it that is greater than the cost of acquisition + maintenance of the asset.
The “code is not an asset” argument is phenomenally ignorant about what assets are, because every element of that argument is perfectly normal for non-cash assets.
"Don't use recursion to solve a problem easily solved by a for-loop, except when recursion is the right tool for the job."
You're not wrong. Just not terribly useful advice when fighting against real complexity in systems.
Can anyone point me to real examples for C# or Java?
I think it's okay to skim over the Haskell specific parts; what I really liked how the end result (version 10 of the program) is nicely composed out of orthogonal pieces, compared to version 1, which is a monolithic blob, while "simple". So, if you want to solve a related but different problem, you can reuse everything you've written instead of duplicating code. So any bugfixes/improvements to one part can trivially be propagated to all other use cases.
[1]: https://blog.acolyer.org/2016/09/14/why-functional-programmi...
Best software practices book I've ever read, even though I don't use Java for a while now.
It's FULL of practical examples. Most of the book is examples, and it's not a small one.
Java for Everything[1]
Too DRY[2]
And then there's a third, which is not quite the same topic, but still in the same vein. It's arguably even more relevant today than three years ago when it was written, especially given how much attention is being captured by ecosystems that attract folks who end up generating a morass of terrible tooling:
Helping my students overcome command-line bullshittery[3]
1. https://www.teamten.com/lawrence/writings/java-for-everythin...
Furthermore I have been using Golang and would say it is very simple language that anyone could pick up and become productive with quickly. One of Go's proverbs is "Clear is better than clever."[2] At the expense of a little verbosity there is much less ambiguity in the intent of code.
1. https://www.infoq.com/presentations/Simple-Made-Easy 2. https://go-proverbs.github.io/
A great mind shift there.
Every team I've ever been on [albeit that's only two], it's the opposite, if the devs had their way they'd spend all day every day simplifying and refactoring. The only reason we don't, is because actual business needs demand attention.
No, they did do that, that's where the layers of bloat originated from. Everything turns out to be not quite as generic as the authors thought and that helper function end up as it's own class with an inheritance tree, etc. That fancy mapper that uses reflection to eliminate hundreds of lines of boilerplate becomes a liability because information is now lost to future maintainers.
I don't want less code, I want simple, greppable code that allows me to trace data across the codebase.
How do you sell that to people?