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

Good code rarely needs to change because it's complete. It's meant to be built on top of, rather than modified for every new consumer. Think standard libraries. There is no reason for the linked list module to ever change unless it's for bug fixes or performance improvements.

Business logic needs to change all the time, because businesses are always changing. This is why we separate it out cleanly, so it can change easily.

Know what type of code you're writing so you can plan and design appropriately.




Meh. Good code is a weasel term. Code can be easy to extend. Easy to change. Easy to throw away. Sometimes tradeoffs are made between those options.

This is like saying a good piece of furniture is easy to add to. I mean, maybe?


To my mind, one of the important aspects is that code must be easy to understand, so as to write it correctly.


Every virtue is easy to argue for in isolation. All else being equal, who wouldn't prefer their code to be easy to read? Or easy to extend, performant, simple, small, secure, packed with features, well tested, and so on.

The trick is that writing code thats easy to understand often takes more time. Making code performant will often make it harder to read, and harder to change. Adding lots of features will make your code harder to change.

Its easy to point at the virtues of good code. And its easy to pick out some personal favorites. But the difference between an intermediate and expert programmer is knowing how (and when) to trade those values off against each other. When you're prototyping, write messy code that you can change easily. When you're writing a web browser, agility doesn't matter as much as security and performance. If lives depend on your code (eg in medical, rocketry, etc) then testing becomes a lot more important. Working with a lot of junior engineers? Try and write code they can understand and maintain more easily. And so on.

Its a fine thing to have a personal style when programming. But the mark of excellence is whether you can adapt your style to suit the actual problem you're trying to solve.


I like the idea of thinking about various code components as furniture!

In practice you can get by with a wild collection if non-matching furniture. It will be non-asthetic but it will work.

Code projects are quite similar in that regard. On the other hand sometimes it will be such a poor fit that it starts breaking things. Does that also apply to furniture?

Perhaps if you put the bed too close to the closet you can no longer open its door all the way...


There are also furniture pieces that are made to connect to each other. Some are obvious like peg boards, but things like sectional couches, too.

Then there are the myriad array of cabinet doors. And folks like heavy built-in cabinets, but Ikea ones are just fine. They typically have better hardware. But nobody wants to be the equivalent of an installer in software, it seems.

Since you said beds, consider getting the wrong size bed for a room. Or mismatched head and foot boards.


Hard disagree. I wish the world worked that way, but it doesn't. So much happens in the time between the product inception and delivery. Initial proposal of value, prototypes, customer feedback and requests, known performance issues to work around, making hooks for non-devs to execute important overrides... the list goes on, and contracts have dates that have to be met. Stuff can't be rewritten to address every turn. This is why Agile (however abused it is) largely beat Waterfall. Software should serve people and yield to their needs. Not the other way around.


I don't know what you're disagreeing with, but I suppose you're welcome to open up a PR to change the standard library with your specific use case so you can meet a contract date. I imagine you'll be quite handedly laughed at.


Many companies I worked at had forks of stanrdadd libraries internally


I've seen this often as well, typically some library will get flag by programs like snyk giving you a "high" score. The way snyk scores packages is completely asinine. It favors libraries that are constantly being updated compared to say some library that is feature complete and in maintenance mode. One way around this is to literally pull all the source code and paste it into your repo.


Same here. And every time that was a mistake and the fork sucks.


Standard libraries was one example you cited for a broad claim about "good code". I'm not arguing about standard libraries.


I have replaced pieces of C++ standard library quite a few times.

std::list calls malloc/free for every node instead of a bunch of them, expensive. Also they are doubly linked, for some applications a single-linked lists fit better.

std::vector<bool> lacks data() method, makes serializing/deserializing them prohibitively expensive.

Even something as simple as std::min / std::max for float and double types aren't using the correct implementation on AMD64, which is a single instruction like _mm_min_ss / _mm_max_sd.


Was going to swoop in to say this!

I would add that sometimes leaving oneself room to expand/respond to changes is just what your code needs. Expansion points. Whatever you want to call it.

The maxim I use is, "avoid being overly specific." If you have polymorphism in your language the less you say about the type of a variable the more places it can be used and the more ways it can be composed with other functions. This requires a style of design that pushes side effects to the edges of the program (consequently where they're the easiest to change).

With this style of programming responding to change is straight-forward to reason about. No need for complicated indirection between objects and tracing behaviors through v-tables. If you are using OOP keep your data and behaviors separate.

The stuff that solidifies rarely changes. The stuff built on it changes a lot. And as time goes on you'll find that refactors will start pushing more upper layers down where they will eventually solidify.


Pointing to a list makes the problem look too easy, because it’s such a clearly defined abstraction. Of course a linked list mostly doesn’t need to change—it’s an easy problem.

Give me a definition of good code that applies to:

- the standard library

- a gui toolkit

- the kernel

- the apps built on top of vendor provided gui toolkits that change

- a web application backend

- a web application front-end

- a database

- more things I’m forgetting


Corollary of that is that MVP for library code is very different than MVP for business code.

MVP for business code is a great way to get the tool in front of the users and get traction, request for more work. Once you release your library, desire for changes basically drops to 0.

It's working. If it's clunky, the clunkiness just gets wrapped into a utility class somewhere deep in the belly your client application with about 1 commit change per year to change the copyright notice.

Similarly your corporate leverage falls to 0. You make a library to save people time, congrats you did it. Every update you ask people to do that does not bring new feature they need reduce your value. Good luck justifying a cosmetic change ROI.


If you make a good code and build a bunch of things on top of it, but find out it wasn’t matched to a use case and need to move to a new architecture, that good code is going to be a pain to remove or change. “Good” shouldn’t be conflated with “everywhere”. A lot of bad code is everywhere because fixing it seems too painful.

The linked list module may have a poor interface or an awful bug with workaround. But you can’t change it until the next version of the language in 3 years because everyone relies on it. It’s not good, just a dependency. It needs to change and can’t. I want devs to embrace that changing heavily-relied upon things can be good even if it’s painful.


If you switch from int to float, you don’t “refactor” or “extend” the int type to be a float. You build a new system and switch over.

If you switch from mergesort to quicksort you don’t “refactor” or “extend” mergesort. You write quicksort and replace the calls of mergesort to quicksort.


> Good code rarely needs to change because it's complete.

Maybe. But on the other hand 99% of code is not good, and thus is more likely to need to change. And that is a reality that we need to deal with unfortunately.


I don't think people think like this enough: which is why you will get bespoke double entry accounting code mixed up with all the other business logic in almost every application that needs it.

The idea of making an abstract double entry accounting module that is extensible it's own thing is rare. It's probably Conway's law at play. There is no one responsible for doing so, the teams were set up with their goals and everyone shares that code.


good code is code that gets you promoted, increases your networth, and helps you retire faster

depending on your work environment, good code may actually be bad code.


Here! I'd say it is most of the time the bad code.


Most any code can be library code given enough growth in a business.




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

Search: