Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Give up on “good code”. Its pursuit is how junior devs pesters senior devs based on a delusion that such a thing is possible.

Good code is working code, code that pays the bills. Focus instead on writing code you can throw away easily, code that you are wholly unattached to and is isolated enough that rewriting it won’t cost absurd hours.



I disagree.

The problem with believing that "good code" is good enough to deliver business value is short-sighted.

Good code is highly maintainable so that you can continue to meet business objectives in a timely manner without regressions. Often this means (ironically) taking a little bit of extra time early on to think about how to make your code readable and "simple enough" for someone else to be able to jump in and maintain it.


The problem with this “maintainability” argument is the presumption a) that it will be maintained (note I recommended rewriting frequently) or that b) it’s in conflict with meeting business objectives.


Rewriting throws away years of accumulated edge cases handling. Suddenly the thing doesn't work because one particular model of printer needs an undocumented command to enable some feature, or users are inputting bad data because you forgot the checks you accumulated...

Seems not ideal for end users, unless you're working with microservices or something with well defined specifications that people are actually paying attention to.


Depends on the field, but what I work on generally evolves so much over time that by the time I'm ready for a rewrite, the "edge cases" I had to account for when I started are either solved, partially solved, or can be isolated into some tiny part of the codebase that could be ported over from the previous code.

Beside, "rewrite" here doesn't mean "new repo, new project, new everything" it means reimplementation, usually based on the lessons learned from the previous implementation, and that does include edge case handling, as well as expanded functionality to "underwrite" or justify the effort spent on the rewrite.


Rewriting does not need to be an all-or-nothing endeavor.

If you design your app as a collection of subsystems, it's sometimes faster to destroy and rebuild one of them than it is to refactor.


Rewriting frequently (reference a) is often out of the hands of the developer because of (reference b) business objectives. Writing code is entirely in the hands of the developer only at the time of writing, not a "henceforth and forever" sort of situation.


And I suppose a dev who only cares about paying the bills would still resonate with this advice. It's not their problem once the suits take charge of the product and give a thumbs up. Why bother with future dev maintenance? the suits will pay for that bride when it burns down, and having that happen is 100x more easy than trying to explain good code upkeep that delays business for a few weeks.


> Focus instead on writing code you can throw away easily, code that you are wholly unattached to and is isolated enough that rewriting it won’t cost absurd hours.

> Good code is highly maintainable so that you can continue to meet business objectives in a timely manner without regressions.

I think you essentially agree on what people should do, regardless of whether you call this 'good code' or just maintainable code.

Write tests. Give things good names. Use comments to explain why you're doing something, not how to program. Don't copy and paste the same implementation x times because that way there's only one place to fix it.


> Don't copy and paste the same implementation x times because that way there's only one place to fix it.

But also sometimes it makes more sense to copy and paste over trying to fit an abstraction where it shouldn't be. :)

I think the purpose of questions like the ones by OP is not to figure out "rules" (which are useful only for beginners) but to figure out where and why rules were broken. Sometimes (often) the answer is time, but that in and of itself is a useful example.

Good intermediate (I suppose Sr. in our industry) level code is notoriously difficult to find examples of and mentor toward.


>But also sometimes it makes more sense to copy and paste over trying to fit an abstraction where it shouldn't be. :)

depends on goals. diverging modules should be copy-pasted, two modules that rely on the same functionality should be consolidated (not necessarily abstracted, but synchronized somehow). Those are both two common cases, so there's no general advice on which is better.

>Good intermediate (I suppose Sr. in our industry) level code is notoriously difficult to find examples of and mentor toward.

so much industry code and knowledge is proprietary, so I imagine that is by design. even intermediate code has a bunch of value to a company, even if the company lets go of that engineer to make their earnings report 0.1% higher.


> But also sometimes it makes more sense to copy and paste over trying to fit an abstraction where it shouldn't be. :)

I find that's a smell of limited languages: Maybe a language has poor error handling semantics, maybe it's not expressive enough to make a parameter generic.

It can also be a smell of not understanding the language well enough, too. Maybe there is no need to copy and paste, but the programmer didn't understand the language well enough to make a generic abstraction.


I agree. I've had the unfortunate experience of moving from more expressive languages and interesting problems to less expressive languages and boring problems as the size of my TC and company go up. :')

But you can only do what your tools allow you to do.


"maintainable" code is disposable. Modern web stacks are built on the idea that you can delete a file and re-implement its interface with a different service behind it later on. Instead of writing code which will be easy to modify, write a good interface which solves your problems in a way that's easy to reason about, and feel free to write garbage implementations that will get "refactored" (thrown away) every year or so


>Modern web stacks are built on the idea that you can delete a file and re-implement its interface with a different service behind it later on.

and that paradigm is exactly why my domain is the start opposite of front end web development. Code I write may be used by thousands of other engineers over decades. I can't take into account every edge case, but I do try to write with the assumption that one day some archeologists will uncover that code as some Rosetta Stone. Of course, modern demands never let me reach that ideal, but it teaches care and good documentation.


You can write good code behind well defined interfaces, too. There's no reason you have to pick one or the other. Bad implementations are still bad.


I usually find it faster to define a good interface, hack together and implementation, then rewrite it.

A good interface limits the blast radius of my refactor.


This is what "top down" coding looks like. In "top down" coding you're told what to do by the boss, given too short a timeline, and forced to push something out the door.

Congratulations on figuring out how to create a massive churn rate among your actual good engineers.


Give me engineers who value solving problems, sometimes with code, over “good” engineers every single day of the week.


I understand you're characterizing the word "good" as some bad definition of "good" used by some subset of others, but your quotation marks are doing a Herculean amount of lifting (i.e. it's hard to tell what you're saying). I think most people would consider "engineers who value solving problems" as "good" and vice versa.


I'm just using "good" here as defined by the parent comment I replied to, to make the point that I believe the engineers as he described wouldn't value solving problems as much as they value "good" code.


It goes hand in hand. We're still talking about engineers here, not pure computer scientists. And if we want to call them "engineers" they should understand short and long term ramifications of any decision they make, something reflected in the code they are responsible for. Few other engineers would ever accept a paradigm of "make fast, re-implement fast", but since that's a luxury a software engineer has, it comes more down to understanding what the business needs and using the right tools.


Taken at face value, this advice is probably just as bad as the opposite ("write perfect extensible modular code with 100% documentation and test coverage"). There is a lot of room in between where the enlightened developer can find happiness.


You misunderstand; nothing about what I suggest says you shouldn’t write, “perfect extensible modular code with dull documentation and tests.”

If that’s what you need to do to solve the problem, do that. The point is to stop focusing on the code as the work product, and instead focus on the solution as the work product, of which code is one part.


These are the same thing.

Good code should always solve the solution, to be considered good code. Bad code might solve the solution, it might not.


If you're at the start of a company writing an MVP, then you're spot on. If the code isn't being written for a startup in the early stages, this doesn't make sense.

Writing bad code that just "gets it done" is the proverbial broken window. It's how you end up with shitty code-bases that get shittier with every change, until it all collapses under its own issues.

Doing this on established code-bases is basically what the classical duct tape programmer does. Sure, you deliver "business value" in the short term (normally to claim credit and gain favor) but at the expense of everything and everyone else.


Please send me your juniors, I'd like to hire them.


I’m not sure if this is an insult or compliment…


I took it as a compliment. Some value "productive" engineers, while others value "curious" ones. curious ones are probably for companies that want to retain talent and invest a lot into R&D. productive ones are for when you need to get a product out fast before anything else.

Not saying one is better than the other. Sometimes being first across the line is make or break for a company. But I wish companies could be more honest about what they want.


This mentality is how you end up inheriting terrible codebases.


I would rather write “bad” code that lasts long enough to be inherited than write perfect code that sits idle in a repo because I missed the market.


Writing good code doesn't necessarily take more effort than writing bad code; in fact, my experience tells me otherwise. Teams that write good code iterate faster.


The point I'm trying to make is that the pursuit of "good" code is futile, as it does take more effort to achieve than writing functioning code.

But in a way you're right; if there is no to ensuring your code follows more conventions, go for it. That's exceedingly rare, however, as a situation to be in.


What property of the "good code" allows them to iterate faster?

Is it because it's perfectly abstracted and encapsulated and SOLID and DRY?

Or is it because it's written in a manner, easy to understand, easy to extend, and easy to throw away and rewrite if necessary?


Everyone's opinion is it's based on the code they actually work with(And devs have lots of personal projects which adds bias, because they think stuff is a good idea just because it works on 1000 line projects)...

I'm guessing solid and dry are a big part, but lack of cleverness, language choice that doesn't require cleverness, and heavy reuse with libraries and frameworks, and choice of feature complete libraries is probably a lot of the speed.

Something in C is going to be more work than JS or Python or Dart and work takes time. Code you write takes more time than code that already exists, unless the code that exists disappears one day.


>Or is it because it's written in a manner, easy to understand, easy to extend, and easy to throw away and rewrite if necessary?

This. But IME when you write for this purpose you tend to end up with code that is in fact confusing, not documented, and not test covered. So you need to slow down even if your goal is to one day completely re-write that module.

Perfect abstractions are never perfect unless you completely architect out the product, down to every single edge case. That's virtually impossible with a large codebase, be it due to an API or even compiler level bug.


It's abstracted a little bit, but every abstraction is designed to lift a roadblock and reused multiple times like an old tool. And special effort has been spent on avoiding technical debt (hard coded stuff, untested stuff, overloaded or missing properties) to the point of doing things with less effort.


>because I missed the market

This is not relevant to the vast majority of developers, realistically (and is an important caveat to your original comment).


It absolutely is, though many people would agree with you.


Statistically speaking, most developers are not working for startups. And of course, large companies have the sway and politics to go around not being first. e.g. Apple isn't worried about missing the market, they are trendsetters and they light a market up even when 10 years late to it.


Your “market” can be an internal customer.


Most people would, I'm sure.

On the other hand, those aren't the only two choices, by a long shot.


it's strange seeing someone with absolutely no pride in their craft.


The craft is solving the problem, not endlessly circling around it. perfect is the enemy of good


My craft is not leaving a dumpster of a solution to the next people to look at my code when business logic inevitably changes. If someone's solution to seeing my code is "we gotta re-write it", I have either failed or a much more novel solution was discovered that makes my code irrelevant. I hope it's not the former.


Your “craft” is not to write code, it’s to solve problems. If you can’t be proud of the problem you’ve solved, you are no engineer. That’s the main difference between an engineer and a scientist.


Sure, and a civil engineer can solve a water leakage problem with duct tape. Real prideful moment.

As I said, it depends on the project. I'm not going to approach a leaky faucet the same way I would an industrial sewage pipe. Fortunately the industry is big and I can choose to work on larger problems where longevity is valued over throughput. You're fine whipping together a React App in a weekend While I'd be more the end of maintaining the React repository. To each their own.


I am guessing that your definition of "problem" is a bit too narrow and simplistic.

> Your “craft” is not to write code, it’s to solve problems.

Writing code is part of solving problems.

The quality of code has an impact on the various aspects of how a group of people solve problems.

As a small example - consider onboarding time for a codebase/system. Longer onboarding time means - lower profits end of the day for the organization (I'd also argue that longer onboarding times correlates higher talent attrition). And code quality has a strong influence on onboarding time.

Code quality has an impact on the "debuggability" of your systems. How quickly can you fix stuff when things go wrong?

Code quality has an impact on the "deployability" of your systems. If your code is well-done it is easy to deploy, redeploy, etc.

I can cite maybe 10 more properties crucial to org health, which are influenced at least partly by code quality.

So, "the problem" is not as simple as it may seem at first glance.


Or maybe “code quality” really means how visually pleasing the code is, or maybe it means it takes up the least amount of disk space, or what if it means code that doesn’t use the letter ‘e’?

This is why the pursuit of “high quality code” is pointless; you are not an artist, you are aligned to solve a problem. If you do that, code or not, you are doing good work as an engineer. If you are not, you are not. Whether the code fits some arbitrary definition of “good” separate from your ability to solve a problem doesn’t play into it.


You refuse to define the problem in a comprehensive way in the first place. That's the meta-problem :)

The second issue is that you think it is impossible to define an "abstract good" in code quality given a specific context (team, product, market). The "abstract good" stems on its own for the given internal culture and market situation. Through some common sense examples, it is easy to see how "abstract good" wields influence on "practical parameters" critical to business survival/thriving.

As an analogy, I can say the "body is healthy". I am aggregating a bunch of metrics to say - "this is healthy". It doesn't mean the term "healthy" is meaningless. The term "healthy" has useful meaning although not at a mathematically precise level. One could even argue that the term "healthy" captures something even precise mathematics cannot capture (it's abstracted at a higher level). Apply similar argument to the term "code quality".

Edit: Maybe it is better to explore the idea of code quality "via-negativa". Find what's actively harming beneficial outcomes. And remove it. If you cannot find many harmful things, then it has high code quality.


You’re falling for the trap I’m suggesting you avoid. Stop caring about platonic ideals of what Good Code ought to be, and start focusing on how well the code solves the problems it was built to solve.

You can call that good code if you want, but my argument is to stop caring about the code’s “quality”, as a value it carries independent of the problem.


It's not platonic, my argument is empiric.

The physician - operates "via negativa". He tries to find faults with the given body, tries his best, and when can't find - he calls it "healthy".

The engineer/businessman can look at the code from an empirical point of view.

If onboarding is bad -> code is bad

If understandability is bad -> code is bad

If deployment is bad -> code is bad

And so on. As you eliminate these issues, your "code quality" increases (just like as you eliminate disease, the body becomes more healthy).

Look into say, Taiichi Ohno's Toyota Production Management - one associates "zero defect" ~= "quality". So, the term quality alludes to a continuous elimination of faults and shortcomings.

The aggregate placeholder/banner term 'code quality' stems from very firm practical sources, that can be inspected, amended and improved.


Sure, but you are in agreement with what I’m saying; good code implies specificity towards the line-by-line writing and structuring of the language (that’s what “code” is, more or less), and I argue that’s useless, as do you here by citing examples of how the code solves the larger problem in the system.


I think coding is a balance of 2 problems

The first is the business problem, most important.

The second is the problem of maintaining and iterating on the solution to 1…

Without solving the first problem, the business dies and you don’t even need to care about the second problem… however the second problem can also kill the business if not solved eventually…


I kind of agree, reminds me of a good quote from somewhere…

> make it work, then make it pretty, then make it performant

Solve the problem first, then improve the solution


The whole point of the rules in the second paragraph are that you can then fix it without needing to replace the whole thing in one go!


I am a self taught LAMP guy that wrote a mini saas that 34 companies pay for and use. It pays the bills, and works surprisingly fast compared to most CRMs, but I can assure you, it is not good code. Even I'm pissed how shitty I let it get.


every journey starts with a single jira. -C#onfucius


"write code that is easy to throw away" is generally a good piece of advice


With the caveat that "easy to throw away" means "easy to understand the implementation front to back so you can know the full impact of throwing it away without crossing your fingers"


I don't know about front to back, a good module should be explicit on what it affects and _ideally_ abstract its implementation. We were talking about what a module should aim to be, and that aim is a good proxy for a lot of properties of well architected applications, even when not perfected. And even when you don't plan to ever throw the module out.


Good is a direction.

A painter might study and practice enough to be as good as Michealangelo, but the attempt to be 'more good' or 'better than Michaelangelo' stops as soon he starts a serious painting. He has to finish the painting with the skill he has at that point.

I agree code should be unattached and you can throw it away. As soon as you start coding a program for a client, your attempts to become 'more good' as a coder and to write more ideal code, have stopped and it's time to make some code you can throw away or sell to the client.

But all the 'training' paintings you made on the journey to becoming 'good' have to be kept, because you trashed thousands of attempts along the way in pursuit of an ideal painting. The good painting is framed and hung on my wall. The commercial painting is sold to the client or trashed.

Yes, give up on 'good code' at work. Keep the ideal of good code as a direction to improve towards, not an end result in commercial works.

As soon as 'good' became explictly defined/bike-shedded it died anyway..... it's an infinite direction, not a limited thing that can be defined and boxed up.


Comes down to semantics, really. Is "good" equivalent to "acceptable" or does it mean "to be strives for"?

Code that works is, usually, acceptable. But code that is acceptable while making no unnecessary maintenance trade-offs is much better. Good code is code that uses standard techniques in standard ways to achieve a result without being verbose or inefficient. But that is a much higher bar than code that is literally good enough.


In general agree with the sentiment not to obsess over perfection, but not sure it’s great advice for someone looking for code to read and understand.


>isolated enough that rewriting it won’t cost absurd hours.

I mean, juniors can do this. Once you're a senior, there will inevitably be come coupling you need to make in order to "pay the bills". Or you may make the first part of a system that will be a pain to re-write, even if it's the most elegant, readable code ever.

Nothing wrong with preparation.


Good code "is isolated enough that rewriting it won’t cost absurd hours." This is the hard part.


Yes, and it also is the point I think most people here are missing.


This is uncharitable. The GP's comment is short. What people have an opinion about is, "Give up on 'good code'" and, "working code, code that pays the bills ... code you can throw away easily, code that you are wholly unattached to".


big claims require big justifications. I can't just go out and say "C++ is better than Javascript" and expect people to not scrutinize me because "well it was a short comment".

But hey, they do justify it in responses. So maybe they are indeed playing to their philosophy of "work fast"


I would say a take on this is good code is code that is worse than the next code you write. Don't pursue perfection, pursue improvement over time. Make things work but learn new stuff that makes your old stuff look foolish. Improvement is more important than perfection.


Separately, if you won’t be dissuaded from this pursuit, check out redis.


So much this. Good code is code that delivers business value.


Also, good code is code that respect constraints and objectives from test suites


I would amend this just to “delivers value”.


Where business value has a time dimension.


Sounds like you've been surrounded with bad code all your life. This isn't true.




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

Search: