Hacker News new | past | comments | ask | show | jobs | submit login

Overall a good article, but I completely disagree with the notion that "good enough is good enough".

I've been in a lot of code reviews where developers push back because it's "good enough". You need to maintain a defined level of quality otherwise codebases go to shit very, very fast.

I was recently told in a code review that a Cassandra read before a write (to ensure there were no duplicates) was "good enough" because a duplicate "probably wouldn't happen very often". Meanwhile, the consequences of a dupe would lead to a pretty bad customer experience.

I pushed back hard and forced the developer to rewrite his entire code. Would "good enough" be okay in this situation? My bar is much higher than this developer and I stand by my decision. We have the luxury of being tasked with solving customer problems and if we only strive for "good enough" every time instead of "the best I can do within the constraints I'm given", then in my opinion your career won't be very successful. We always have to make the best tradeoffs when it comes to time and expense, but the best developers are the ones that come up with the best solution and the best code that fits in a particular constraint.

Hey! OP here. I definitely don't mean "good enough is good enough" as an excuse -- pushing for quality is extremely important.

My point was more about nitpicking line by line for perfection. What you're talking about sounds like a legitimate performance issue.

I think we're on the same page, but maybe my point wasn't clear enough. I tried to make it clear in my last point that "code is quality is important" but it's important not to confuse code quality with things that are more minor like idiosyncratic coding style.

Thanks for reading!

When I code review, I differentiate between "code quality" and "opinion". First off, there needs to be a coding standard guideline so that 80% of all issues of "opinion" vs "coding standard" are well defined. This is one of the reasons why I like go, because things like go fmt are the great equalizer.

I probably won't like the variable names people chose but I won't comment on that because that's "my opinion". I will comment on even the smallest bug I see, because that's what we're paid to do. So line by line "perfection" is what I believe we need to strive for in terms of code quality. Maybe not so much "perfection" but "best practices" might be a better way of stating it. We always need to strive for best practices so that our code is predictably easy to maintain, read, etc.

Agree with this. I obviously comment on bugs and also convoluted code that I know will be hard to understand later. If it is just small details it is easier to just change them myself when I inevitably have to revisit the code later on rather than nitpicking and arguing during the review. If I don't have to revisit the code for any change then just let it be. Out of sight, out of mind

Another way to skin this cat I'd say is that the definition of "perfection" or "best practices" is different depending on the use-case, company, product, or mission. Making sure you have a very clear definition of quality and "done-ness" is really important here. For some products any bug is a no-go. For others, lots of small visual bugs are ok, and on and on. But once you agree on that definition of quality, I totally agree with you that everyone needs to champion that definition.

I'm going to challenge you here (admittedly without full context, but to make a point). It sounds to me like the problem you're describing requires a database has ACID transactions which is not Cassandra. Therefore Cassandra is a poor choice and in my view that is poor engineering. This being said, I'd err on the side of agreeing with the developer who determined that they are not capable of writing code that guarantees ACID transactions in Casandra (that's a hard thing to do), and I'd really question the choice to use it to begin with.

Again without appropriate context, my bet here is that you guys are using Cassandra for other important features you won't get out of a typical RDBMS and as such you made a trade off to begin with and decided that Cassandra was "good enough".

Now the point I'm trying to illustrate (and I'm not just doing this to pick a fight, I promise), is that engineering is about trade offs and a big part of it is definitely related to likelihood of a problem occurring.

I think it also completely depends on the domain of the problem, the criticality of the process you're building and the outcome of a major failure of your assumptions.

So I'd just argue and say "good enough" is an entirely appropriate answer in many contexts and domains and it's important not to make a blanket assumption that it's wrong.

We aren't in disagreement, and I don't take your comment as a challenge, it was exactly something we went through with this feature. The point is, who gets to decide what "good enough" is? The difference between my "good enough" is a lot different than my coworker's "good enough", and I think that's what separates levels of maturity as a developer.

In fact Cassandra wasn't my first choice, but a strongly consistent database wasn't available to us. As I mentioned in another comment, making the very best decision you can given the constraints of your system is what one should strive for. Not stopping at "good enough" because of (poor) intuition that error conditions "probably won't happen".

We decided to go with LWT and eat the latency costs as a trade off to "stronger" consistency, realizing that Cassandra doesn't offer the same strong consistency as an ACID database. Not perfect, but it fit within our SLA, decreased the probability of encountering duplicate values, and if there was an error, it was easier to detect and the user could be directed to try again, vs having a completely silent error condition that would cause a small percentage of our users tremendous amounts of trouble.

That was my thought, too. If you need ACID transactions, use an ACID database. There are rather a lot of them. And then mirror the data out to Cassandra after the fact, if you need it there. Ironically, Cassandra may not be the source of truth!

Way back in the stone age, MySQL did not yet have ACID transactions. They got them about the same time they stopped bragging about how much faster they were than Oracle, but I digress. Anyway, I had to write a bunch of transactional code around it. Drove the dba and me nuts. We begged for Sybase (we both knew it well), but the startup CTO was an open source purist and hated his first contact with the Sybase sales machine.

Eventually they folded, and the point was moot.

> You need to maintain a defined level of quality otherwise codebases go to shit very, very fast.

And it's that level that we call "good enough". Or, I would say, acceptably bad.

One of the most important lessons I've learnt over my career is that there is no such thing as "good" software. Everything could always suck less — anything that takes over 0.0 seconds is bad, more than 0kB of memory is bad, more than 0 lines of code is bad. However, your level of badness for each of these metrics might be something you're willing to live with.

It's like hygiene. What you call "nice and clean" for your toilet is not clean enough that you'd cook on it, and even your "immaculate" kitchen is unacceptable for, say, an OR. Hygiene is always "bad", you're just looking for a point where it's no longer unacceptably bad for your purpose.

Codebases all go to shit pretty fast. That is really what you learn as a senior developer. The fully sustainable codebase is a myth. All codebases will inevitably get progressive more difficult to work on no matter what you or anyone else does.

All software eventually gets rewritten. Either in full or in parts. So "good enough" means "will this keep it going until this software, or piece of this software, is thrown away and replaced". Because that is typically much more economical than code review infighting causes 2-4 rewites of every feature until its perfect. Or spending 3 times more time on a feature to make it perfect. Or having to hire very expensive developers that are capable of writing to that high standard.

There are obvious exceptions in specific industries, but this holds true for 80%.

I have experienced great difficulties with literal spaghetti code written in delphi which one can only follow through debugging, but I have also seen large codebases written in kotlin and python where almost everything is in layers and it is obvious where to add a new feature and how to find one that doesn't work. And I was very afraid that the project would end up an entangled mess, but luckily, it had the right mixture of individual services (for scaling), monoliths and messaging mediums to not become a mess.

I understand what you are saying. I think what she's really referring to is: The perfect is the enemy of the good. You have to adhere to certain level of standards, but at some point you do have to admit there is a difference between, "this has problems", and "it's not how I would have done it, but it works and is more or less OK."

Good enough is by definition, good enough.

It's up to the team and customers to decide on that however. Database integrity is particularly important, so with limited information, I'd say you made the right decision. Therefore the first draft of the code was not good enough.

So, we should all be in agreement now, right?

Right, but who decides what "good enough" is? If I weren't code reviewing, then it would have passed as "good enough". And that's the point of why just saying good enough isn't good enough. There should be a threshold that no one fights over.

He gave the threshold in his comment though.

> It's up to the team and customers to decide on that however.

You said yourself:

>the consequences of a dupe would lead to a pretty bad customer experience.

I've seen many situation were a duplicate wouldn't matter to a customer. It would matter to me because like you, I'm a perfectionist, but at the end of the day, it's both the team and customers that decide together.

It's also a matter of dealing with the problem now while it's right in front of your eyes and you remember.

Generalizing here but assume 6 months later this rare duplicate happens for a very important customer so you can't just brush it off, you now really have to fix it. By then nobody remembers this code review so you don't even know if this duplicate is a one off rare event or if it is going to affect all customers. Fixing it in code review might have taken a few hours extra for one guy, now you sent the whole team scrambling weekend overtime just to find the issue and understand the implications.

> "good enough" because a duplicate "probably wouldn't happen very often". Meanwhile, the consequences of a dupe would lead to a pretty bad customer experience. [...] My bar is much higher than this developer...

I see a lot of posturing in this anecdote, what I think is missing are:

1 - an indication of how often would a customer experience the issue;

2 - how bad would his "bad experience" be;

Did you calculate the former and took in account the latter in forming your judgement?

Or, otherwise, was the "correct" solution simple and obvious enough that any non-junior developer would have picked that first without hesitation?

You're getting some flack because "good enough" is too loaded and everyone reads it a little differently. No one seems to be reading your qualifier, "the consequences of a dupe would lead to a pretty bad customer experience."

Sometimes, good enough is good enough. If you were able to push back hard in this case, I take it you are senior to the other guy and your decision is/was justified by the product/feature requirements. IOW, in this case, good enough was in fact not good enough.

> constraint

most important thing, right there.

I don't think anybody would argue pushing bugs is good enough or qualifies as such under any circumstance. I think good enough means that no codebase is perfect and working with nitpicky perfect types is the absolute worst IMO. Not only that, I've seen those codebases too and they are often times just as convoluted and messy, although without trailing whitespace and no missing semicolons.

There's the "it'll do" mean of "good enough", which really isn't good enough. Then there's the "of sufficient quality" meaning, which is by definition good enough. The latter is the true minimum bar, IMO, and what I believe the author means?

I also think that a blatant disregard for correctness is not good enough. For me the good enough is more around style and architecture. If there’s an obvious bug, even if it only affects .1% of users it should be fixed. After all, if you have a million users your bug now affects 1,000 people.

Well, it's very clear that what the developer was trying to do was not "good enough."

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact