How to Become a Bad Developer
rafaelquintanilha.com
rafaelquintanilha.com
My experience has been the opposite. Novice developers tend to be proud of how terse and dense their code is, and optimize for minimizing line count.
I also disagree with that author's views on this point. For example: "Simply put, it means “the less, the better”" - No, it isn't.
In my career I found that maintaining unsexy, and maybe a little verbose code is never the problem. Usually that code tends to be more easily understandable to even junior developers and therefore is less prone to bugs being introduced because developer made the wrong assumptions on what the code was doing. Code that is too clever for its own good is where bugs tend to occur.
Often even sacrificing everything to that tersness.
Clearly defined purpose for each layer when doing abstraction.
Verbosity and simplicity.
And NEVER EVEN try to be smart. In two weeks you will forget how the 'smart' solution works and have to spend x2 time changing it.
I definitely agree with that, and I think a preference for more verbose code is actually very compatible with this idea. Verbosity is not the same as over-complicating or over-abstracting things. In my experience a lot of the time it's actually the drive to avoid verbose code that causes these problems in the first place.
For instance, you have two functions that are similar but with some important difference, and the developer decides that anything that resembles duplication is bad and refactors them into a single abstraction. This type of thing can cause much more harm than just leaving in a little bit of verbosity by keeping the functions independent of each other.
If so, then by keeping them separate, you introduce the risk that you would forget to update the other. Using a single function and calling it twice, even with different parameters, is a way communicating that it's important that the semantics be tied.
If a change in one wouldn't imply a change in the other, then you really have two separate semantic roles being played that happened to resemble each other in implementation - harmless incidental duplication.
If there's a really obvious answer to that question, then great. But often the answer is "sometimes" or even "most of the time". We train most developers beginning even in intro-level programming classes to err on the side of deduplication and over-abstracting in these cases, but the longer I've been working as a developer the more I have come to think it's better to err on the side of allowing some duplication except in the most clear cut of cases.
That is to say, novices can tend to pile onto a solution with hacks and special cases instead of examining why their abstraction requires so much special casing.
I think the code golf you're talking about is a different (but not exclusive) thing.
They can write some of the tersest code once they learn some new thing ("Wait, C# lets me use LINQ and lambdas. I can turn this entire thing into 3 lines of code!") and some of the most convoluted messes when they run into an issue.
I call the latter the epicycle approach [0]. A simple model that doesn't quite work, but they can't let go of, is complicated by adding more and more "simple" extensions but it produces a convoluted mess that is nigh impossible to maintain or sustain.
[0] https://en.wikipedia.org/wiki/Deferent_and_epicycle#/media/F... : Demonstrating the complexity needed to maintain a geocentric model and account for the movement of celestial bodies.
One way to write less code is to in practice code golf. To follow DRY add deeply nested abstractions. To not have to plan out what you do, use YAGNI or "no premature optimizations". Etc.
Edit: My personal favourite is "code should be self-documenting", i.e. comments are a failure.
When people first start coding they're trying to get machines to do things. They have to learn the techniques of making code for other humans. To them it's making things verbose and adding tons of comments because that's better.
Meanwhile, they're usually learning to plan for future code needs and make their code flexible.
But part of learning to use those techniques is overuse. So that means code gets gold-plated, baroque, byzantine, and needs a novella of comments to explain its magnificence to future developers so that they use it right. Time to trim it back. Less is more.
I think that's what the author meant.
I like the way you phrased that.
Anecdotally I find code that is too clever annoying at times, when time is short and the PM Melissa has a bolt action rifle to my head on friday afternoon. I get that code that is too concise is also trying hard to be marvelled as genius-worthy.
https://rafaelquintanilha.com/how-to-become-a-bad-developer/...
The author lists the habits of *bad* developers as: (1) Never assume there is a bug in your code, (2) Write code without reasoning, (3) Lack assertiveness, (4) Take pleasure in writing more code, (5) Write for machines, not humans.
So the author advocates for less code, but written to be used and maintained by other people.
For some(especially novices) that means plodding, low abstraction code, so that all the details are visible. This makes for longer code. Others see high abstraction code as the cleaner simpler code because the abstractions hide what they see as irrelevant details, which makes for terse code.
“the less, the better”
“the more, the better”
Tautologically both are incorrect when pursued farther than is prudent, and "the right amount, the better". Unfortunately no one really agrees on what "the right amount" is or we wouldn't have to discus different definitions and strategies to reach "the right amount".
In my experience the problem with juniors is more about thinking about the problem in a very linear fashion and not stepping back to see it as individual parts.
For example, I need to know when a user takes an action. So, I go into the code where that happens and throw in a bunch of analytics vs having a messaging system where events are raised and the code that handles those events are completely separate workers that are simple and easy to understand.
— Miyamoto Musashi, The Book of Five Rings
It is interesting that you think that code with more abstractions is easier to understand.
From my experience the simple linear beginner code is the one that is quite easy to work with. It is easy to grasp the general intention and to slowly refactor parts of the code if need be.
Now a mid level developer that thinks of themselves to be smart and tries their hardest to use some fancy abstractions and patterns? Yeah, put it in the trash. We will have to rewrite that code.
The best code is from the senior that finds the right balance. Finding the right abstractions to use takes years of experience.
The most important rule when programming is: Don't try to be smart. Keep it simple.
And it’s why io_uring is a stark improvement over select.
The problem is bad abstractions. Bad abstractions don’t cut the underlying problem across clean axes, they leak too many implementation details, and they aren’t often able to be reused. “Linear beginner code” that handles too many concerns in one place is inarguably bad, but bad abstractions are generally worse.
We shouldn’t be trying to stop using abstractions: virtually all of the history of progress in computing has been driven by finding good abstractions that allow us to build bigger and better things with less mental overhead. We should be doing absolutely everything we can to understand and teach the practice of writing good abstractions.
It often helps to be specific about which traits you are/aren't focused on when arguing for the "right" solution. Words like "easy" and "simple" are more ambiguous and lead to confusion.
These differences in object and system visibility sometimes reflect specific use cases (e.g., a professional kitchen can operate faster with open shelving than with opaque cabinet doors). Other times, they simply reflect the personal preferences of the designer.
And that nuance means for example, maybe it makes sense to create a helper to append some additional error detail to a string if used only twice, but maybe if some similar behavior is used in 7 slightly different ways, it shouldn’t be generalized if it requires lots of conditionals and arg plumbing.
This is why I kind of hate the mantra of “if it’s used 2-3 times or more, wrap it in a function or class.” IMO the bar for something being wrapped needs to be higher - either the amount of logic should be large and segmentable enough that it can be placed in something that separates concerns from where it otherwise would be, small and very repetitive, or it needs to be used much more often in a way that truly resembles a library (and thus requires an interface that does not expose implementation details or wrap messy conditional logic).
I think that’s a fine signal to use to identify that something needs to be refactored, but that doesn’t absolve an engineer of having to think about how to actually do so.
It’s why I’ve always cringed at IDEs with “refactor selected code into its own function” actions. This is so rarely what you should ever be doing.
Refactoring is like butchering. You need to figure out how the muscles lie and separate them on their natural boundaries, not just start hacking things into parts because they seem too big. Once you’ve separated pieces across good functional boundaries you can start trimming and breaking things up to fit specific needs, but it has to start with stepping back and getting a wider view.
Both incredibly dense and incredibly verbose code can become maintenance headaches. You want it short enough that it can be contained in your head, but long enough that the steps are obvious.
I liken it to the scene in Return of the Jedi where Han is instructing Chewie to fly close, but not too close.
* any original code is NIH
* vetted by the community
* original code can fail
I would agree with that. But then again, it isn't entirely incompatible with the cited quote. That is, at different times in their careers, developers may tend to do different things.
In any case, I think I understand the original quote as something I have indeed seen somewhat frequently in large projects. It seems somewhat common, from my experience at least, that people -particularly "not-so-good developers"- involved in large projects tend to assert how large/important the project in fact is, by mentioning LOCs. "This is a very large project... It's about 5.5MLOCs or so. It will have grown a bit since we last counted."
The more general approach would be to write code with coworkers/audience in mind.
In some environments or projects it might be more effective to write in a way that is harder to grok (requires more work to understand) but is ultimately more tailored to a specific outcome, may that be performance, extensibility, security or what ever.
But every day was a game of code golf to him. His code was absolutely illegible
I do like vocabulary-based metrics for legibility/difficulty; Halstead metrics can't tell you how good a piece of code is, but they can tell you how difficult it is for others to read, empirically.
The old 7 +- 2 rule applies, if you are using more than 9 functions/methods and arguments, the vocabulary of a function is difficult.
f compose g is 0.5. It has 3 elements. It's not hard to understand.
def compose (f: B=> C, g: A => B): A => C = x => f(g(x))
Is 2.5, counting all the definition parts and type level stuff. It has 14 distinct elements. If you don't know the pieces, it's really difficult to read if you don't know any of the parts. If your language doesn't care about types,
def compose(f, g) = (x) => f(g(x))
Comes to 2.1 by my count, but you have to look up fewer things at 7, so it's easy to read for new people.
Now, the types make it easier once you advance beyond a pure beginner- the constrain the function to only one possible implementation. But if you're totally unfamiliar with things, they make stuff unreadable. Even in tiny functions.
Novice developers are frequently proud of the complexity of their code. Experienced developers write simple, easily understood, code.
Inherently, more code means more chances to explain what it’s doing to other humans, so shouldn’t more code be better?
Obviously not, so I take these two principles in tandem as “write as much code as is needed to accomplish the task effectively and explain how it works, but no more than that.”
Squishy, but I think this article as a whole does a decent job of addressing your concerns.
I think it's both. There's a sense of accomplishment that goes along with both accomplishing some small task with minimal code, and also for having written a somewhat large chunk of code for a project. Both have their place, as succinctness has it's place, and it's natural to look back and feel proud of a lot of work. It's just when they aren't tempered by the experience to ask whether that terseness came at a cost that wasn't worth it or whether that project is much larger than it needs to be for the task accomplished that it ends up being detrimental.
Could mean:
1) As you said, terse clever code and agree 100%.
2) Or avoid writing factories that produce factories, abstractions upon abstractions that never get the payoff since the abstraction only gets used for a single (or a handful) of cases ever. When you write abstractions, make sure that in next 10-20 years its going to get its money's worth by taking advantage of it. There are always some exceptions but most of the time I've seen code that has no defense for all the bloat.
3) The best code is the code that you don't write. In this case "the less, the better" indeed.
So I can see multiple meanings of "less, the better". Also, my experience matches the author - new developers are mostly proud of number of lines than cleverness.
This is one piece of developer lore that’s actually backed by some research. [1] Like it or not, there is a correlation between lines of code and bug count.
[1] I’ll link to this research when I have a little more time. Edit: Here's a great resource on this: https://www.hillelwayne.com/talks/what-we-know-we-dont-know/
I discovered IRC as a teenager in the 90s, and soon learned SMS-speak for rapid messaging. I was proud of how quickly I could write if I just wrote "u r" instead of "you are"... This was cool! In chatroom arguments, being able to belt out more sentences faster was better!
But as my typing improved, I realized the above was a childish optimization. Real skill was able to write out correctly-spelled sentences just as fast. I then saw the SMS-speak as immature shortcuts.
This realization carried over into code: good code is the clear, adequately verbose (but appropriately concise) kind. Code that tries to be overly-clever (of the "code golf" kind), was to be viewed with suspicion.
As I’ve gotten more experienced, my code has gotten less fancy and more verbose. A single terse expression is broken out into 3 or 4 statements, defining variables that capture and name fragments.
But yeah, otherwise really enjoyed the article!
In my short career, the worst code was always the one made by the overly experienced CTO that insist on doing everything in a one liner.
Depends on the culture. A lot of offshored programmers will report their "performance" as number of lines written. With the expected results...
I've never seen a senior write nested ternary expressions with side effects...
Maybe I conveyed the wrong meaning on this sentence. I don't think that less code is better. I believe that the more code you have, the more potentially breaking parts you get. However, the right amount of code is a function of your deadline, team, goals, budget and whatnot.
In any case, it's nice to have principles as a beacon during your development, so you don't get lazy and fool yourself. But by no means they are written in stone and every attempt to summarize an absolute truth will be shallow.
I've seen very talented developers fall prey for that one. They seem to fit the pattern of your typical "hacker" type that grew up spending all their youth on coding, and thus binding a lot of their identity and self-worth to their coding abilities.
They blaze through college, being extremely competitive among their peers, and are often among the "go-to" students in class, which only boosts their ego even further.
Then they join the workforce, and find themselves having to work with other people - and they hate it. Any hint of criticism is taken deeply personal, like a sledgehammer to their self-worth. Tips and tricks from others are equally bad - who are they to teach you anything? After all, you've been banging out code and reading books since you were 9.
Code review is like walking on eggshells for the others - they know you'll roll your eyes, chuckle and snort, and just shoot down any incoming comment.
If only you didn't have to work with all these morons around you - the very people that are slowing you down. Move fast and break things, isn't that the motto?
I have a team leader right now who blocks all of my PRs and always demands rework, despite the code functioning exactly as intended and being performant and efficient as well, and maintainable too.
At some point you've got a job, and code alone is not the whole job. Can't do part of it, then you're not doing a good job.
A job is almost never just code.
At first I thought I was just hopelessly bad at coding/following instructions, but even when I pair programmed with these folks and followed their every instruction, they'd still want to refactor all of it the next day.
Some people are just never content unless it's 100% written by them.
I sometimes do that with contracts drafted by junior lawyers — and often it's due to having had additional thoughts after seeing an actual "product," in sort of a fail-fast mode of drafting. Could that be part of the explanation in your case?
I've dealt with this before by documenting everything they told me to do differently, with date and time.
Always fun doing code review in a group, having the lead point out a number of problems they have with the code, especially actual bugs, and pulling up the changelog which includes notes from the one criticizing your code, telling you to make those design choices. I've collapsed whole meetings this way, with people walking out and going straight to HR with requests to transfer teams. Really helps to remove the poison from the group.
var string = "";
instead of
String string = "";
My teammates waste their time focusing on the most inane things.
There are members of my team whose PRs are rarely approved without rework. There are other members of my team whose PRs almost never require rework.
It's just a gut check question. This guy could be totally wrong and a jerk. Or he could be right and a jerk. If he's right and a jerk, then internalizing that feedback might help you (even if he's still a jerk.)
I make sure to thoroughly document my changes in my commit messages and sometimes in my comments, especially documenting who requested the change so that there's a nice paper trail. I also push back often, but the mere act of responding to feedback that does not seem honest from a team lead is traumatic.
My problem is: how do you report such a passive-aggressive person? A single request or question on its own seems benign and not particularly harassing, but the overall trend is a huge red flag. To be honest, I'm not sure much can be done.
If you decide for whatever reason that you cannot leave, the best way to deal with a passive aggressive person is to erase from your memory that they are passive aggressive at all. It is a test of wills that you will win, just don't lose your cool. Never forget that a passive aggressive person is a fundamentally insecure person, and then you will always have the upper hand.
This is genuinely just an example of bad management and bad workplace politics. If the code is working and checks all the boxes, nobody in the organization should be allowed to say "I personally do not want your code to be deployed", especially if other developers dont face such restrictions.
The simple solution is to leave, but I am growing tired of job hopping. If I can land a job as a lead, or at least a place that has autonomy, I am pledging not to do such things to those "beneath" me, as if that word was even true.
With good mentoring, I've seen a few of these so called "hacker" types transform after a few years. Also when you are 20 you're into one thing -- competition for the ego -- and 10 years later into something else -- collaboration for the group. Responsibility and experience are great transformers.
But someone has to actively give that feedback, and the "hacker" type has to be willing to learn, it's true. Some engineering leaders lack the courage to do so and this really is where the problem starts.
I've seen people reading about CQRS, Clean Code, Hexagonal Architecture, Event Bus, all these "good practices" published by celebrities... and after some time these people believe "they are right" because they know these patterns and because they apply them every time code needs to be written. It's difficult to reason with them; the conversation usually goes this way:
- Joe: We should use Hexagonal Architecture in our new system because Alistair Cockburn knows what's best
- Me: I think we don't need it? Past experience in other projects of ours says we can handle this new system without HA
- Joe: I don't think you know better than Alistair Cockburn, so let's stick with HA
Many architectures and techniques (CI/D, TDD Unit Testing, etc.) are based on large projects, with multiple developers, being reassigned and rotated frequently. They often can be applied to smaller projects, but their effectiveness is not always on par with large projects.
I tend to write alone. That means that my scope is, out of necessity, much smaller than many people enjoy, but it also means that a lot of infrastructure overhead is actually a severe problem.
The advantage of writing alone, is a flexibility that many teams could only dream of, and having a lot of team overhead management practices and structure in place, absolutely kills flexibility.
The Terse vs Verbose code debate is important and all, but honestly architecture is the thing that can make or break a project.
Picking the wrong architecture for the job is the kinda thing that delays features, brings more bugs to the table and will make the exact same people ask for a rewrite down the line.
I once worked on a company that was dominated by such types. They made terrible architectural decisions out of cargo-cult and it took two backend rewrites in a row to get things half-right, but they still ended up with a mongrel codebase with several mixed complex patterns.
In your example, your real-life experience matters 100x more than Joe's opinion of whatever architecture he wants to use.
If you're doing distributed cloud event based systems these are sane ways to go about it. I contend though that usually you can get away with a series of big fat SQL databases until you're well into the billion in revenue a year club.
Sometimes it's medical quackery, other times it's economic policy orthodoxy or religious moralism and sometimes it's software stuff
It's fundamentalism. I've gotta go back to working on that book. It's all the same shit and enters and exits people's heads through the same emotional and environmental triggers
The set of patterns that you mention are a different paradigm from the typical OOP-based paradigms. They come from people who are looking at code and seeking composability. As they come at it from a different perspective, when an OOP-programmer comes at the patterns they will cross a "weirdness budget" limit and superficially disregard it.
Any architecture - in a Turing complete language - can solve any problem. All programmers should be seeking to keep complexity to a minimum, while also being well understood. Complexity is made up of two elements, essential complexity that all problems have, and accidental complexity. Accidental complexity can arise out of all sorts of ways, and skill of the programmer, maturity of the architecture, and many others.
Switching to a different architecture that you are not familiar with will cause accidental complexity. Switching to an architecture that has not been well tested will cause accidental complexity.
I believe that CQRS, Clean Code, Hexagonal Architecture, Event Bus are all patterns that will eventually be proven to be correct for most people in most situations, but that the tooling for all of them is not yet there. You should evaluate the patterns for yourself and test them out and find what you can take away that works today. I do not currently use all of them myself even though I believe in their long-term suitability. I do the same for OOP frameworks. The only way to know anything is by trying it yourself and getting to know it.
If I ever hear folks name-dropping, that line of discussion ends and we need to reign it back in to the real "choices and trade-offs" discussion that will get us a real solution, not some abstract opinions.
Very common trap. Most of us grew up with a natural enjoyment of programming, so it’s easy to lose sight of the fact that our job as engineers isn’t to produce more and more code. Our job is to deliver results, not lines of code.
This often manifests as developers or entire teams that can’t stop rewriting, refactoring, and rearchitecting code that is already good enough. They might want to rewrite their otherwise working service in Rust because Node.js is no longer popular, or rework the stable React app to use hooks, or fork and maintain a popular open-source project because they think they can improve it. They prefer to write generalized solutions or frameworks from the start instead of writing a specific one-off solution to get the job done. Or maybe they just have perfectionist tendencies and struggle to submit that PR until they make just one more improvement.
This isn’t unique to developers, of course. My last company had a major problem with UI/UX designers who could never finalize their designs. We lost countless months to endless iterations and tweaks. Part of the problem lies with management, of course, if they’re not enforcing actual project deadlines and end times.
I've also found that management is actually the greatest source of UI/UX reworks; once they've seen the pixel perfect mockups, management thinks that "the job is done" and they want to iterate ...
I work as a backend engineer now because I just couldn't take those meetings anymore.
I've dealt with a product that was so poorly architected that, to this day, it is constantly causing production outages, bad data, etc. And the root of the problem is that it wasn't built to do the right thing. At it's very core, it processes and stores data incorrectly. (Leading to "there are good days and bad days and ops will handle it").
Yet, I met the "We CAN'T REWRITE" pushback, which, ok, but here's the solution that doesn't involve a rewrite, it'll be 10x more expensive because the slow evolution will be a whole lot harder and more risky than a fresh solution with an adapter to the old interface.
I certainly don't take rewriting lightly, and don't generally suggest it, but I have to ask. How can you sell it when the alternative is what my company deals with today?
Yeah, sometimes you have to rewrite/rearchitect but more often you don't. But sometimes the job is to actually get some one to let go of the control of strange baby they implemented so you can rewrite.
Years ago I worked on DNA analysis software where the entire thing was abstracted; codons could be any number of bases instead of just 3, there could be any number of bases instead of 4, there was a whole system for managing amino acid names that assumed there could be an infinite number of amino acids. Loading a sequence involved millions of checks to see if there were any matches against 4 base codons or 5 base codons ...
That definitely was a rewrite; but the developer fought it every step of the way.
In order to write a good application the process it is going to support needs to be well documented. This wasn't the case and I had to go back in circles a few times but you live and learn.
We got tons of code that I'd love to rewrite or refactor. But it works. Has done for 15 years and will continue to. Instead I can fix proper bugs and add new features.
Sure if the old mess is standing in the way of delivering something important I will do something about it. But not until then, we just tweak it when needed and that's it.
Or you get branding lunatics involved who aren't able to produce and provide visual-design guidelines that they trust well enough not to need to stick their nose into every project to make sure the button corner radius is "on-brand" and can't ever let a design pass without some kind of tweak, because that means they're doing work, I guess.
But too much refactoring doesn't really make sense because you cannot see where chaos wants to go, so you'll constantly find local maxima.
I believe we often have to use heuristics and "best practices" as guidelines for refactoring, structure and design. However one of the best methods I find is to just observe for long enough where problems start to arise. Like pumping a liquid through a thing so you see where it leaks if that makes sense.
Should code that is delivering results, but is hard for others to understand & work on be refactored?
My misfortune was being assigned to take over one of the projects he threw together. My god, what a mess. He literally had 10 to 20 boolean flags in every pages long functions so that it was extremely difficult to tell what path the code would take under what circumstances. I got fired because I couldn't understand the code enough to enhance it. I was judged inadequate.
A couple of years later I had lunch with the developer who took my place. She told me what code she was working on. When I casually said "Oh, that spaghetti code is still used?" she let out a big sigh. She thought the problem was with her and finally someone had come along to call out the crappy code this "rockstar" was foisting on everyone.
Devs that churn out more features are generally perceived more favorably than devs that churn out fewer lower maintenance features. There's no linking to "The guy that original wrote the code wrote a mess".
The ironic part of it was I fired because of a bug in his code. One of the flags he was using was set wrong. It cost the company a contract worth a few hundred thousand dollars and since I owned the code at the time it was my fault. I didn't even try to argue because programming jobs were easy to find at the time and I wasn't sticking around to deal with their mess.
There's this phase any every developers career, my own included, of "being just smart enough to be dangerous". It's like, you're smart enough to use those kind of features, but not smart enough to realize that just because you can doesn't mean you should.
The worst part though is, when you take it over, the expectation from non-technical people is "well this guy wrote the feature in a weekend, why can't you update the feature in a weekend"? So you end up looking worse in comparison because your predecessor set you up to fail. I would implore anyone writing code that has some empathy: try to setup the people working on your code for success.
yeah, but that would take more than a weekend..
My manager showed me where I fall on the company-wide distribution of quality(based on issues attributed to my code), and quantity(based on logical lines of code committed). I was near the top for quality, and near the median for quantity. My project had been fairly complex, involving the joining of two already complex systems that handle a wide range of configurations and had their own fair share of bugs that I had to figure out. I held my tongue when they said that I should work on increasing my output.
(I mean, unless you don't want to do that sort of role, of course.)
I've noted code where a previous fixer obviously didn't figure out what was going on, and instead bolted their solution to the side of the codebase instead of figuring things out. I spent a good six months after go-live on my last big project removing the bolt-ons and following the patterns, leaving consistent code.
Committed LoC: not much, bad if quantity proves worth; actually removed more than I added on that clean-up job.
I should mention that the "rules" there are completely opinionated and reflect what I understood as the biggest problems I had (and still have) as a software engineer.
Also would like to clarify that "via negativa" is a general principle, but of course won't work all the time. As a matter of fact, I refrain myself from excessive modularization whenever I can. In the end, software development is about delivering the biggest value, in less time, without compromising quality and maintainability (or at least being aware of the costs).
A nice side-effect of this semi-virality is that I get to read a lot of nice additions to the original 5 points. This post is calling for a sequel...
I had a boss who went with, "Always assume the bug is in our code." Which wasn't too bad, except for the time he wouldn't let us tell the hardware partner they probably had a bug until we could definitively prove that it was their problem and not ours. Took us a few months to produce a reliable test case for the error (consistent failures and not sporadic ones) and shifted the entire project schedule (not just us) right. He just didn't trust us lowly CS types.
We could make it "work" in that it would send the correct data but it was too slow to operate within the protocol specs (so the whole system would fail, hitting time limits that caused resets and test failures), or we could pray that all the correct data made it which it would 7 times out of 10. But that 30% (or more) failure rate meant that we couldn't reliably determine when a failure was in our software or the hardware.
But for 99% of interactions, you want to assume you're the one who screwed up. It's safer and it's better customer support.
A similar rule to this is to remove the phrase "it works for me" from your vocabulary. The client wouldn't be complaining about it if it worked for them! So you need to rule out a bug before you go back and say "Hey, actually, you're the one who screwed up." I've found that maybe only 50% of the time is it client error, and so I always operate under the condition that the customer was right until I can prove that they weren't.
I agree with the author's reasoning that the number of lines of code you write is meaningless and not something you should hold up with pride, but there is a flipside to that: when you get to a certain amount of experience, code verbosity is usually a good thing.
At some point when learning, many programmers become good enough at thinking in code that they become able to express everything in incredibly terse one-liners. This is when they start using ternary operators everywhere instead of if statements, or very obscure functional map/reduces instead of simple for loops.
Being able to write like this is obviously good, but that doesn't mean you should do it: just because you can cram everything into one line/statement, doesn't mean you should. This gig is not just about writing clever code, it's about writing code that others can read (as well as yourself, six months later) and reason about and debug easily.
Writing understandable code is not always the same as writing more verbose code, but the two are certainly correlated.
I started off writing code in a procedural style with no organisation. I was told that was bad so I created fragmented abstracted code where everything did very little across 10s of classes and methods.
Now I write procedural style code again, I don't worry about long methods as long as the flow is obvious and the abstractions, used sparingly, feel right.
The functional programmer in me just died a little. Map and reduce are not obscure. They’re the basic building blocks!
For loops, now that’s obscure. There’s like 5 different incantantions for a for loop in JavaScript alone. Each with its own subtle behavior differences.
Not every problem benefits from a functional solution.
Sometimes people make complicated incantations inside for loops (or worse, while loops) that could be replaced by simpler functional constructs. In these cases, map and friends are easier to read.
The problem here is not one construct or the other, but the fact some people are making code that is hard to understand. There's no one size fit all solution.
Are we talking about languages without handy combinators? I can imagine that in these, turning stateless functions into stateful aggregators is going to be more obscure rather than easy.
As for speed, that seems like an implementation issue. In principle a functional solution could still preserve more information for efficient code generation than a loop for which such information needs to be extracted from by complicated auto-vectorizers. In practice the environments aren't "quite there" (lack of such features as Common Lisp's compiler macros), and any potential or even realized benefits may be cancelled out by other performance issues of such environments.
At the end of the day though, it's still your issue, even if you can point at a 3rd party. Personally I'd rather have my fate in my own hands rather than wait on a "sufficiently smart optimizer"
Conceptually basic != basic for humans. The more basic and "simple" the maths, the harder it becomes for human beings to understand (otherwise most people wouldn't fail first years of higher education maths that often).
- Stop caring about understanding what you are doing.
- Stop caring about the quality of your deliverables.
- Stop caring about understanding how things work.
- Stop caring about finding better ways of doing things.
- Stop caring about learning.
- Stop caring about what you have learned and how you can use it.
There are many factors that will make tempt you stop caring, but you should resist them and persevere. One of such factors is: mediocre jobs, mediocre leadership.
If you stop caring, you'll become unemployable in a few years, and any advantage you had over younger developers will go away.
I can't tell you how many bugs I've fixed because programmers write code that only works if everything goes exactly the way they expect.
Mid-level developers spend enormous effort modeling the error conditions and their programs never crash but are 10x longer and have lots of subtle bugs, especially edge cases of data corruption.
Senior developers ignore the non-happy path and ensure the program crashes a lot.
Callbacks everywhere. Confusing use of compositions - A is in B but also B is in A, making it impossible to reason about.
nowadays, 2 biggest advise I get from articles and books are: (1) make code as readable as possible (2) write code so it is easy to remove the code and re-write. basically advising me to write such methods: StoreUserDataAndFetchDetailsFromOauthProvider, instead of some fancy flows
world is moving very fast and time spent on making code modular with design patterns seems not worth it (I am spending my last 6-8 years in startups).
most of my code is verbose and doesn't contain design patterns, am I that bad developer?
Readability has a long tradition in programming too, including but not limited to literate programming (1984). IMO nothing wrong with verbosity as long as it serves a purpose (readability is a worthy purpose). I think the perceived trend "developers care more about readability now than they did x years ago" is likely due to the proliferation of click-bait blog posts on the subject.
Lastly, if you're asking yourself the question "am I that bad developer", then IMO you by definition aren't that bad developer. Bad developers don't ask this question, or they don't care about the answers. You are self aware!
maybe yes, maybe no. depends on how much engineering effort was spent. if upfront cost of perfect decoupling is high and later we need to delete it, why think about decoupling it at the first place? maybe write verbose, spaghetti code and when code contains all the business logic, then start refactoring slowly, when things became stable?
I lost count how many times I had to fight over doing actual work and coming clean on how much is really done versus making the result only appear finished and knowingly merging buggy code.
Stakeholders put pressure because of course they do. That doesn't mean lying to them is a sound tactic. It's much better to negotiate a reduction of scope than pretend you've delivered only to later have them, or worse - the users - discover the truth.
Step 2: Don't ask questions, don't know how to communicate
Step 3: Take ideally 1 day or more to respond to clients
I could see this being a "good developer" trait too. For example, writing programs that are designed to be easily consumed by other programs. Humans might like a nicely formatted HTML page, but machines like plain text output.
If you meant to say that machines will parse content faster in plain text because there's no HTML parsing necessary then you're right, but you're also missing that there are far better formats than ascii text for passing data from one machine to another, and what format to use really depends on what the data is.
But, that's a last resort problem solving approach, and should be the exception not the rule.
I would prefer a developer who can work with a large amount of code. I would prefer, even more, a developer who can write the same thing with less code. Generally speaking, a developer doesn't gain the ability to the do the latter without first doing the former.
And there's an annoying phase in the middle where "less code" is condensed code.
I cannot stand that approach. The real tell is that none of them have ever used a raw html element in any of the code they have written. It is more or less custom components on top of something like material-ui or bust! Yikes.
So in one company you can be a good programmer, in another you could be a hindrance.
Oh, and to echo another comment here, they just LOVED cyclomatic complexity.
"Don't worry about failure conditions" - Just code everything to the happy path
If you work in a team or a joint repo...
> So if you never assume that you might have blown things up, you will start blaming other things – your peers, the stupid framework you are using, an outdated browser or a pre-historic OS. Anything will be responsible but you. And if you never admit your mistakes, you are cursed to never evolve. And not evolving as a developer is fatal.
If your test suite is comprehensive I've found it's often the case there's some external variable outside of your control that wasn't accounted for. This may or may not be a bug and may or may not need to be accounted for. Sometimes it's as simple as "upgrade your browser" or "we don't support that input, that is why you get an exception". Just as this person seems keen to blame the developer, it's often the user who blames the developer for their own shortcomings.
> If you lose track of this, you have become a bureaucrat. And well, it’s pretty hard for a bureaucrat to be a good developer.
98% of my job as a senior IC is bureaucratic nonsense. No one cares what code I write. No one cares about elegance. They care about a solution on time, and most importantly on budget. This is a failing of nu-agile where engineers are micromanaged to death by "certified PMs".
> But even if you don’t, you are halfway through to the solution and a fresh pair of eyes will be much more effective in the process of helping you.
"Be assertive" is such a poor way of saying "use rubberduck debugging". Especially in the context of writing code.
> Take pleasure in writing more code
There have been many times I've gone out of my way to make my code more verbose (within reason) to increase clarity for my juniors. I don't do this as a habit but again "all things with a grain of salt".
> It’s pretty clear that your workload is proportional to the amount of code.
This is so untrue I don't even know where to start. I can make a counter example 10 line function that needs 30 tests. In fact, a lot of real algorithms are this way. Going by line count/test ratio is a bad developer habit and this guy is coming off like a bad developer.
> Think about what makes a text enjoyable to you. It is usually concise, clear, direct, meaningful and consistent. You won’t enjoy reading when you can’t understand the author
This sentence is wildly ambiguous. On one hand the author advocates writing clear, meaningful, and concise code, and on the other hand they advocate (by omission) being as verbose as possible all the time. I make frequent use of higher order functions to stop cluttering my code with simple loops for sums, operations over a list, etc. Would the author consider me a bad developer because not everyone understands these simple constructs?
> I hope that you find the above rules useful in your quest of becoming a bad developer. But if you ever change your mind and decide to grow into a good one instead, well, you now know what you need to avoid.
Ah, hell hath no fury like the hubris of a bad developer telling other developers how to be good.
wich is, unironically, the purpose of the title haha
> Always try to understand what’s the purpose of the task you’ve been assigned to.
Shouldn't that be "Always try to understand what the purpose of the task you’ve been assigned [to] is" ? Is this a new trend in (American) English?