News for hackers. Stuff that matters.
Re:It's probably time to stop recommending Clean Code (2020) by dham on Tuesday May 25, 2021 @10:25AM (#27277151)
Yes let's please do this. I'm tried of this book being brought up at work.

My clean code book:

* Put logic closest to where it needs to live (feature folders)

* WET (Write everything twice), figure out the abstraction after you need something a 3rd time

* Realize there's no such thing as "clean code"

Re:It's probably time to stop recommending Clean Code (2020) by anonyfox on Tuesday May 25, 2021 @10:30AM (#27277249)
> WET (Write everything twice), figure out the abstraction after you need something a 3rd time

so much this. it is _much_ easier to refactor copy pasta code, than to entangle a mess of "clean code abstractions" for things that isn't even needed _once_. Premature Abstraction is the biggest problem in my eyes.

Write Code. Mostly functions. Not too much.

Re:It's probably time to stop recommending Clean Code (2020) by allo37 on Tuesday May 25, 2021 @10:43AM (#27277428)
I think where DRY trips people up is when you have what I call "incidental repetition". Basically, two bits of logic seem to do exactly the same thing, but the contexts are slightly different. So you make a nice abstraction that works well until you need to modify the functionality in one context and not the other...
Re:It's probably time to stop recommending Clean Code (2020) by fiddlerwoaroof on Tuesday May 25, 2021 @11:11AM (#27277854)
If you mostly deduplicate by writing functions, fixing this problem is never very hard: duplicate the function, rename it and change the call-site.

The interesting thing about DRY is that opinions about it seem to depend on what project you’ve worked on most recently: I inherited a codebase written by people skeptical of DRY, and we had a lot of bugs that resulted from “essential duplication”. Other people inherit code written by “architecture astronauts”, and assume that the grass is greener on the WET side of the fence.

Personally, having been in both situations, I’d almost always prefer to untangle a bad abstraction rather than maintain a WET codebase.

Re:It's probably time to stop recommending Clean Code (2020) by jakelazaroff on Tuesday May 25, 2021 @11:18AM (#27277948)
Conversely, fixing duplication is never hard. Just move the duplicated code into a function. Going in reverse can be much tougher if the function has become an abstraction, where you have to figure out what path each function call was actually taking.

Or, put another way: I'd much rather deal with duplication than with coupling problems.

Re:It's probably time to stop recommending Clean Code (2020) by bluGill on Tuesday May 25, 2021 @03:11PM (#27281089)
The problem with duplication it is hard to spot and fix. Converting liters to ml or quarts isn't hard, but the factors are different, and there is also other units. If you only do a few of these isn't a big deal, but if you suddenly realize that you have tons of different conversions scattered around and you really need to implement a good unit conversion system it will be really hard to retrofit everything. Note that even if you have a literToml, Literto Quart and MileToKm functions retrofitting the right system will be hard. (Where I work we have gone through 4 different designs of a uber unit system module before we actually got all the requirements right, and each transition was a major problem)
Re:It's probably time to stop recommending Clean Code (2020) by munificent on Tuesday May 25, 2021 @01:15PM (#27279521)
> Conversely, fixing duplication is never hard. Just move the duplicated code into a function.

I think the single biggest factor determining the difficulty of a code change is the size of the codebase. Codebases with a lot of duplication are larger, and the scale itself makes them harder to deal with. You may not even realize when duplication exists because it may be spread throughout the code. It may be difficult to tell which code is a duplicate, which is different for arbitrary reasons, and which is different in subtle but important ways.

Once you get to a huge sprawl of code that has a ton of mostly-pointless repetition, it is a nightmare to tame it. I would much rather be handed a smaller but more intertwined codebase that only says something once.

Re:It's probably time to stop recommending Clean Code (2020) by jeremysalwen on Tuesday May 25, 2021 @11:43AM (#27278313)
I think the opposite is true. Bad abstractions can be automatically removed with copy/paste and constant propagation. N pieces of code that are mostly the same but have subtle differences have no automatic way to combine them into a single function.
Re:It's probably time to stop recommending Clean Code (2020) by 0xffff2 on Tuesday May 25, 2021 @12:23PM (#27278831)
N pieces of code that are mostly the same but have subtle differences isn't repetition and probably shouldn't be combined into a single function, especially if the process of doing so it non-obvious.
Re:It's probably time to stop recommending Clean Code (2020) by dragonwriter on Wednesday May 26, 2021 @05:46AM (#27288321)
> N pieces of code that are mostly the same but have subtle differences isn’t repetition

Often, they are.

IME, a very common pattern is divergent copypasta where – because there is no connection once the copying occurs – a bug is noticed and fixed in one place and not in others, later noticed separately and fixed a slighly different way in some of the others, in still others a different thing that needs done in the same function gets inserted in between parts of the copypasta, etc. IT’s still essentially duplication – more over its still the same logical function being performed different places, but in slightly different ways, creating a singificant multiple on maintainance cost, which – not literal code duplication in and of itself, is the actual problem addressed with DRY, which is explicitly not about duplication of code but single source of truth of knowledge: “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system”. Divergent implementations of the same logical function are different representations of the same knowledge.

Re:It's probably time to stop recommending Clean Code (2020) by bluGill on Tuesday May 25, 2021 @03:21PM (#27281263)
Often it is. Earlier in this thread I used the example of a unit system - one example of there there can be a ton of repetition to remove, but there are fundamental differences between liters and meters that make removing the duplication hard if you didn't realize upfront you had a problem. Once you get it right converting meters to chains isn't that hard (I wonder how many reading even know chain was a unit of measure - much less have any idea how big it is), but there are a ton of choices to make it work.
Re:It's probably time to stop recommending Clean Code (2020) by jakelazaroff on Tuesday May 25, 2021 @12:36PM (#27279007)
I think they mean if the code does the same thing but has syntax differences. Variables are named differently, one uses a for loop while the other uses functional list operations, etc.
Re:It's probably time to stop recommending Clean Code (2020) by Too on Sunday May 30, 2021 @01:25AM (#27330833)
You never know if these subtle differences were intentional to begin with. It might have been the same once upon a time, but then during an emergency outage someone makes a quick fix in one place but forgets to update all other copies of this code.

Repeating what others already mentioned, often it can be the same thing but written in a slightly different way. Even basic stuff like string formatting vs string concatenation can make it non-obvious that two pieces of code are copies.

Re:It's probably time to stop recommending Clean Code (2020) by fiddlerwoaroof on Tuesday May 25, 2021 @11:33AM (#27278178)
The issue I have is that duplication is a coupling problem, but there’s no evidence in the coupled parts of the code that the coupling exists. It can be ok on a single-developer project or if confined to a single file, but my experience is that it’s a major contributor to unreliability, especially when all the original authors of the code have left.
Re:It's probably time to stop recommending Clean Code (2020) by kaba0 on Wednesday May 26, 2021 @05:40AM (#27288270)
If you find a bug in the duplicated part and has no idea that it was actually duplicated (or even if you do, where are they?), you still have multiple lurking bugs around.
Re:It's probably time to stop recommending Clean Code (2020) by bcrosby95 on Tuesday May 25, 2021 @07:15PM (#27284007)
Fixing duplication is never hard because by nature, duplicated code will drift over time even if it shouldn't have. So it's technically not "duplicate" anymore even if they are supposed to do the exact same thing.

Fixing a bad abstraction is only hard because there's some weird culture about not tearing down abstractions. Rip them apart and toss them on the heap. It's a million times easier than finding duplicate code that has inadvertently drifted apart over time.

Re:It's probably time to stop recommending Clean Code (2020) by fiddlerwoaroof on Tuesday May 25, 2021 @09:26PM (#27285038)
There are certain abstractions that can cause real problems: XML-driven DI, bad ORMs, code generators, etc. But, in general, I agree: people are generally too unwilling to refactor aggressively.
Re:It's probably time to stop recommending Clean Code (2020) by SassyGrapefruit on Tuesday May 25, 2021 @11:16AM (#27277922)
Yep. Re-use is difficult. When you overdo it you cause a whole new set of problems. I once watched a person write a python decorator that tried unify HTTP header based caching with ad hoc application caching done using memcached.

When I asked exactly what they were trying to accomplish they kept saying "they didn't want caching in two places". I think anyone with experience can see that these items are unrelated beyond both having the word "cache"

This is what was actually hanging them up ... the language. Particularly the word "cache". I've seen this trap walked into over and over. Where a person tries to "unify" logic simply because two sets of logic contain some of the same words.

Re:It's probably time to stop recommending Clean Code (2020) by bradstewart on Tuesday May 25, 2021 @11:10AM (#27277837)
So much this. Especially early in project's life when you aren't sure what the contexts/data model/etc really need to be, so much logic looks the same. It becomes so hard to untangle later.
Re:It's probably time to stop recommending Clean Code (2020) by watwut on Tuesday May 25, 2021 @11:16AM (#27277926)
Then you copy function and modify one place? I don't get what is so hard about it. The IDE will even tell you about all places where function is called, there is no way to miss one.
Re:It's probably time to stop recommending Clean Code (2020) by ummonk on Tuesday May 25, 2021 @12:30PM (#27278935)
This, exactly this
Re:It's probably time to stop recommending Clean Code (2020) by pjc50 on Tuesday May 25, 2021 @11:15AM (#27277913)
> it is _much_ easier to refactor copy pasta code

So long as it remains identical. Refactoring almost identical code requires lots of extremely detailed staring to determine whether or not two things are subtly different. Especially if you don't have good test coverage to start with.

Re:It's probably time to stop recommending Clean Code (2020) by macNchz on Tuesday May 25, 2021 @12:13PM (#27278686)
I personally love playing the game called "reconcile these very-important-but-utterly-without-tests sequences of gnarly regexes that were years ago copy-pasted in seven places and since then refactored, but only in three of the seven places, and in separate efforts each time".
Re:It's probably time to stop recommending Clean Code (2020) by tuatoru on Tuesday May 25, 2021 @11:52AM (#27278419)
> requires lots of extremely detailed staring

Yeah, nah. There are diffing tools.

Re:It's probably time to stop recommending Clean Code (2020) by cerved on Tuesday May 25, 2021 @12:24PM (#27278836)
problem is you only diff if things look different
Re:It's probably time to stop recommending Clean Code (2020) by nodoodles on Tuesday May 25, 2021 @11:04AM (#27277745)
> Write Code. Mostly functions. Not too much.

Just wanted to appreciate the nod to good nutritional advice here ("Eat food. Mostly plants. Not too much"), well done

Re:It's probably time to stop recommending Clean Code (2020) by latk on Tuesday May 25, 2021 @11:58AM (#27278494)
Probably a call-back to this post that made the rounds on HN a few months ago: https://www.brandons.me/blog/write-code-not-too-much-mostly-...
Re:It's probably time to stop recommending Clean Code (2020) by cerved on Tuesday May 25, 2021 @01:16PM (#27279533)
There's a problem with being overly zealous. It's entirely possible to write bad code, either being overly dry or copy paste galore. I think we are prone to these zealous rules because they are concrete. We want an "objective" measure to judge whether something is good or not.

DRY and WET are terms often used as objective measures of implementations, but that doesn't mean that they are rock solid foundations. What does it mean for something to be "repeated"? Without claiming to have TheGreatAnswer™, some things come to mind.

Chaining methods can be very expressive, easy to follow and maintain. They also lead to a lot of repetition. In an effort to be "DRY", some might embark on a misguided effort to combine them. Maybe start replacing

  `map(x => x).reduce(y, z => v)` 
with

  `mapReduce(x => x, (y,z) => v)`
This would be a bad idea, also known as Suck™.

But there may equally be situations where consolidation makes sense. For example, if we're in an ORM helper class and we're always querying the database for an object like so

  `objectContext.Orders.Select(e => e.id = y).Include(e => e.Customers).Include(e => e.Bills).Include(e => e.AwesomeDogs)...`
then it with make sense to consolidate that into

  `orderIncludingCustomersBillsAndDogs(id) => ...`
My $0.02:

Don't needlessly copy-pastes that which is abstractable.

Don't over abstract at the cost of simplicity and flexibility.

Don't be a zealot.

Re:It's probably time to stop recommending Clean Code (2020) by macNchz on Tuesday May 25, 2021 @11:39AM (#27278259)
>it is _much_ easier to refactor copy pasta code

I totally agree assuming that there will be time to get to the second pass of the "write everything twice" approach...some of my least favorite refactoring work has been on older code that was liberally copy-pasted by well-intentioned developers expecting a chance to come back through later but who never get the chance. All too often the winds of corporate decision making will change and send attention elsewhere at the wrong moment, and all those copy pasted bits will slowly but surely drift apart as unfamiliar new developers come through making small tweaks.

Re:It's probably time to stop recommending Clean Code (2020) by _huayra_ on Tuesday May 25, 2021 @11:43AM (#27278310)
I worked on a small team with a very "code bro" culture. No toxic, but definitely making non-PC jokes. We would often say "Ask your doctor about Premature Abstractuation" or "Bad news, dr. says this code has abstractual dysfunction" in code reviews when someone would build an AbstractFactoryFactoryTemplateConstructor for a one-off item.

When we got absorbed by a larger team and were going to have to "merge" our code review / git history into a larger org's repos, we learned that a sister team had gotten in big trouble with the language cops in HR when they discovered similar language in their git commit history. This brings back memories of my team panicked over trying to rewrite a huge amount of git history and code review stuff to sanitize our language before we were caught too.

Re:It's probably time to stop recommending Clean Code (2020) by morbicer on Tuesday May 25, 2021 @01:05PM (#27279391)
Wait whaat? I am not from USA and hail from a much more blunt culture, what's wrong with those 2 statements?

To me they're harmless jokes but maybe someone will point out they're ableist?

Re:It's probably time to stop recommending Clean Code (2020) by jasonwatkinspdx on Tuesday May 25, 2021 @02:29PM (#27280436)
Those aren't particularly bad, but dick jokes have no place in a professional workplace imo. I'm so very weary of it. And yes, in the past I was part of the problem. Then I grew up.
Re:It's probably time to stop recommending Clean Code (2020) by _huayra_ on Tuesday May 25, 2021 @01:15PM (#27279529)
It wasn't just these phrases, but we took them out to be safe (thinking that them being jokes about male... "performance", like from pharma ads on TV with an old man throwing a football through a tire swing).

The biggest thing is that we used non-PC language like "retarded" very casually, not to mention casually swearing in commit messages e.g. "un-fucking up my prior commit". Our sister team got in trouble for "swears in the git commit history", so we wanted to get ahead of that if possible.

In a healthy company culture, we'd just say "okay we'll stop using these terms", but the effort was made to erase their existence because this was a company where non-engineering people (e.g. how well managers and HR liked you) was a big factor in getting promoted.

Once I realized how messed up that whole situation was, I left as fast as I could.

Re:It's probably time to stop recommending Clean Code (2020) by icedchai on Tuesday May 25, 2021 @11:59AM (#27278513)
If you were "caught", what would happen? You probably have a couple of zoom calls and get forced to watch sensitivity training videos. Who cares.
Re:It's probably time to stop recommending Clean Code (2020) by dragonwriter on Tuesday May 25, 2021 @01:15PM (#27279526)
> it is _much_ easier to refactor copy pasta code,

Its easy to refactor if its nondivergent copypasta and you do it everywhere it is used not later than the third iteration.

If the refactoring gets delayed, the code diverges because different bugs are noticed and fixed (or thr same bug is noticed and fixed different ways) in different iterations, and there are dozens of instances across the code base (possibly in different projects because it was copypastad across projects rather than refactored into a reusable library), the code has in many cases gotten intermixed with code addressing other concerns...

Re:It's probably time to stop recommending Clean Code (2020) by haspok on Tuesday May 25, 2021 @12:09PM (#27278638)
> Write Code. Mostly functions. Not too much.

Think about data structures (types) first. Mostly immutable structures. Then add your functions working on those structures. Not too many.

Re:It's probably time to stop recommending Clean Code (2020) by throwaway2037 on Tuesday May 25, 2021 @01:53PM (#27279991)
OMG. This is exactly my experience after trying to write code first for 10+ years. (Yes, I am a terrible [car] driver, and a totally average programmer!)

"Bad programmers worry about the code. Good programmers worry about data structures and their relationships." - Linus Torvalds

He wasn't kidding!

And the bit about "immutable structures". I doubted for infinity-number-of-years ("oh, waste of memory/malloc!"). Then suddenly your code needs to be multi-threaded. Now, immutable structures looks genius!

Re:It's probably time to stop recommending Clean Code (2020) by beckingz on Tuesday May 25, 2021 @11:27AM (#27278093)
"Write Code. Mostly Functions. Not too much" made my day and is excellent advice.
Re:It's probably time to stop recommending Clean Code (2020) by UglyToad on Tuesday May 25, 2021 @11:05AM (#27277760)
I think this ties in to something I've been thinking, though it might be project specific.

Good code should be written to be easy to delete.

'Clever' abstractions work against this. We should be less precious about our code and realise it will probably need to change beyond all recognition multiple times. Code should do things simply so the consequences of deleting it are immediately obvious. I think your recommendations fit with this.

Re:It's probably time to stop recommending Clean Code (2020) by Benjammer on Tuesday May 25, 2021 @11:14AM (#27277896)
>code should be written to be easy to delete

This article tends to make the rounds on here every once in a while: https://programmingisterrible.com/post/139222674273/how-to-w...

Re:It's probably time to stop recommending Clean Code (2020) by msluyter on Tuesday May 25, 2021 @11:28AM (#27278102)
Aligns with my current meta-principle, which is that good code is malleable (easily modified, which includes deletion). A lot of design principles simply describe this principle from different angles. Readable code is easy to modify because you can understand it. Terse code is more easily modified because there’s less of it (unless you’ve sacrificed readability for terseness). SRP limits the scope of changes and thus enhances modifiability. Code with tests is easier to modify because you can refactor with less fear. Immutability makes code easier to modify because you don’t have to worry about state changes affecting disparate parts of the program.

Etc... etc...

(Not saying that this is the only quality of good code or that you won’t have to trade some of the above for performance or whatnot at times).

Re:It's probably time to stop recommending Clean Code (2020) by munificent on Tuesday May 25, 2021 @01:17PM (#27279550)
The unpleasant implication of this is that code has a tendency towards becoming worse over time. Because the code that is good enough to be easy to delete or change is, and the code that is too bad to be touched remains.
Re:It's probably time to stop recommending Clean Code (2020) by jasonwatkinspdx on Tuesday May 25, 2021 @03:28PM (#27281356)
One of John Carmack's maxims is "Fight code entropy." As simple as that is it carries a lot of meaning imo.
Re:It's probably time to stop recommending Clean Code (2020) by BurningFrog on Tuesday May 25, 2021 @10:38AM (#27277352)
Re:It's probably time to stop recommending Clean Code (2020) by golergka on Tuesday May 25, 2021 @11:22AM (#27278023)
> * WET (Write everything twice), figure out the abstraction after you need something a 3rd time

There are two opposite situations. One is when several things are viewed as one thing while they're actually different (too much abstraction), and another, where a thing is viewed as different things, when it's actually a single one (when code is just copied over).

In my experience, the best way to solve this is to better analyse and understand the requirements. Do these two pieces of code look the same because they actually mean thing in the meaning of the product? Or they just happen to look the same at this particular moment in time, and can continue to develop in completely different directions as the product grows?

Re:It's probably time to stop recommending Clean Code (2020) by whimsicalism on Tuesday May 25, 2021 @11:57AM (#27278485)
Solving the former is generally way uglier/more obnoxious IMO than solving the latter, esp. if you were not the person who designed the former.
Re:It's probably time to stop recommending Clean Code (2020) by thebackup on Tuesday May 25, 2021 @12:26PM (#27278875)
I read Clean Code in 2010 and trying out and applying some of the principles really helped to make my code more maintainable. Now over 10 years later I have come to realize that you cannot set up too many rules on how to structure and write code. It is like forcing all authors to apply the same writing style or all artists to draw their paintings with the exact same technique. With that analogy in mind, I think that one of the biggest contributors to messy code is having a lot of developers, all with different preferences, working in the same code base. Just imagine having 100 different writers trying to write a book, this is the challenge we are trying to address.
Re:It's probably time to stop recommending Clean Code (2020) by TheCoelacanth on Wednesday May 26, 2021 @03:58PM (#27295338)
I'm not sure that's really true. Any publication with 100 different writers almost certainly has some kind of style guide that they all have to follow.
Re:It's probably time to stop recommending Clean Code (2020) by amelius on Tuesday May 25, 2021 @11:07AM (#27277801)
> WET (Write everything twice)

In practice (time pressure) you might end up duplicating it many times, at which point it becomes difficult to refactor.

Re:It's probably time to stop recommending Clean Code (2020) by Frost1x on Tuesday May 25, 2021 @11:29AM (#27278125)
If it's really abstractable it shouldn't be difficult to refactor. It should literally be a substitution. If it's not, then you have varied cases that you'd have to go back and tinker with the abstraction to support.

It's a similar design and planning principle to building sidewalks. You have buildings but you don't know exactly the best paths between everything and how to correctly path things out. You can come up with your own design but people will end up ignoring them if they don't fit their needs. Ultimately, you put some obvious direct connection side walks and then wait to see the paths people take. You've now established where you need connections and how they need to be formed.

I do a lot of prototyping work and if I had to sit down and think out a clean abstraction everytime I wanted to get for a functional prototype, I'd never have a functional prototype--plus I'd waste a lot of cognitive capacity on an abstraction instead of solving the problem my code is addressing. It's best, from my experience, to save that time and write messy code but tuck in budget to refactor later (the key is you have to actually refactor later not just say you will).

Once you've built your prototype, iterated on it several times had people break designs forcing hacked out solutions, and now have something you don't touch often, you usually know what most the product/service needs to look like. You then abstract that out and get 80-90% of what you need if there's real demand.

The expanded features beyond that can be costly if they require significant redesign but at that point, you hopefully have a stable enough product it can warrant continued investment to refactor. If it doesn't, you saved yourself a lot of time and energy worrying trying to create a good abstract design that tends to fail multiple times at early stages. There's a balance point of knowing when to build technical debt, when to pay it off, and when to nullify it.

Again, the critical trick is you have to actually pay off the tech debt if that time comes. The product investor can't look bright eyed and linearly extrapolate progress so far thinking they saved a boatload of capital, they have to understand shortcuts were taken and the rationale was to fix them if serious money came along or chuck them in the bin if not.

Re:It's probably time to stop recommending Clean Code (2020) by xpe on Tuesday May 25, 2021 @01:09PM (#27279452)
> If it's really abstractable it shouldn't be difficult to refactor. It should literally be a substitution.

This is overstated. Not all abstractions are obvious substitutions. To elaborate: languages vary in their syntax, typing, and scope. So what might be an 'easy' substitution and one language might not be easy in another.

Re:It's probably time to stop recommending Clean Code (2020) by throw1234651234 on Tuesday May 25, 2021 @11:33AM (#27278182)
WET is great until JSON token parsing breaks and a junior dev fixes it in one place and then I am fixing the same exact problem somewhere else and moving it into a shared file. If it's the exact same functionality, move it into a service/helper.
Re:It's probably time to stop recommending Clean Code (2020) by innagadadavida on Tuesday May 25, 2021 @10:41AM (#27277392)
How do you deal with other colleagues that have all the energy and time to push for these practices and I feel makes things worse than the current state?
Re:It's probably time to stop recommending Clean Code (2020) by Spivak on Tuesday May 25, 2021 @10:47AM (#27277502)
Explain that the wrong abstraction makes code more complicated than copy-paste and that before you can start factoring out common code you need to be sure the relationship is fundamental and not coincidental.
Re:It's probably time to stop recommending Clean Code (2020) by bluGill on Tuesday May 25, 2021 @03:50PM (#27281692)
And get ignored or laughed at. Some people know the rules and nothing but.
Re:It's probably time to stop recommending Clean Code (2020) by Chris2048 on Tuesday May 25, 2021 @01:01PM (#27279349)
> figure out the abstraction after you need something a 3rd time

That's still too much of a "rule".

Whenever I feel (or know) two functions are similar, the factors that determine if I should merge them:

- I see significant benefit too doing so, usually the benefit of a utility that saves writing the same thing in the future, or debugging the same/similar code repeatedly.

- How likely the code is to diverge. Sometimes I just mark things for de-duping, but leave it around a while to see if one of the functions change.

- The function is big enough it cannot just be in-lined where it is called, and the benefit of de-duplication is not outweighed by added complexity to the call stack.

Re:It's probably time to stop recommending Clean Code (2020) by KaiserPro on Tuesday May 25, 2021 @11:34AM (#27278192)
repeat after me:

Document.

Your.

Shit.

everything else can do one. Just fucking write documentation as if you're the poor bastard trying to maintain this code with no context or time.

Re:It's probably time to stop recommending Clean Code (2020) by caymanjim on Tuesday May 25, 2021 @12:48PM (#27279174)
Documentation is rarely adequately maintained, and nothing enforces that it stay accurate and maintained.

Comments in code can lie (they're not functional); can be misplaced (in most languages, they're not attached to the code they document in any enforced way); are most-frequently used to describe things that wouldn't require documenting if they were just named properly; are often little more than noise. Code comments should be exceedingly-rare, and only used to describe exception situations or logic that can't be made more clear through the use of better identifiers or better-composed functions.

External documentation is usually out-of-sight, out-of-mind. Over time, it diverges from reality, to the point that it's usually misleading or wrong. It's not visible in the code (and this isn't an argument in favor of in-code comments). Maintaining it is a burden. There's no agreed-upon standard for how to present or navigate it.

The best way to document things is to name identifiers well, write functions that are well-composed and small enough to understand, stick to single-responsibility principles.

API documentation is important and valuable, especially when your IDE can provide it readily at the point of use. Whenever possible, it should be part of the source code in a formal way, using annotations or other mechanisms tied to the code it describes. I wish more languages would formally include annotation mechanisms for this specific use case.

Re:It's probably time to stop recommending Clean Code (2020) by KaiserPro on Tuesday May 25, 2021 @01:02PM (#27279353)
> Documentation is rarely adequately maintained,

yes, and the solution is to actually document.

> wouldn't require documenting if they were just named properly

I mean not really. Having decent names for things tells us what you are doing, but not why.

> only used to describe exception situations

Again, just imagine if that wasn't the case. Imagine people had the empathy to actually do a professional job?

> The best way to document things is to name identifiers well, write functions that are well-composed and small enough to understand, stick to single-responsibility principles.

No, thats the best way to code.

The best way to document is to imagine you are a new developer to the code base, and any information they should know is where they need it. Like your code, you need to test your documentation.

I know you don't _like_ documenting, but thats not the point. Its about being professional, just imagine if an engineer didn't _like_ doing certification of their stuff? they'd loose their license. Sure you could work it out later, but thats not the point. You are paid to be professional, not a magician.

Re:It's probably time to stop recommending Clean Code (2020) by caymanjim on Tuesday May 25, 2021 @02:34PM (#27280511)
> I know you don't _like_ documenting, but thats not the point.

On the contrary, I like writing documentation. What I can't stand is unnecessary documentation; documentation of obvious things; documentation as a replacement for self-documenting code; documentation for a vague ill-defined future use-case that probably won't exist (YAGNI); and many of the other documentation failures I've already mentioned. It has nothing to do with my professionalism. It has to do with wasting time and energy and using it as a crutch in place of more valuable knowledge transfer mechanisms.

Re:It's probably time to stop recommending Clean Code (2020) by Oddskar on Tuesday May 25, 2021 @04:25PM (#27282138)
Self-documenting code is a complete fantasy for everything but completely trivial logic.
Re:It's probably time to stop recommending Clean Code (2020) by scrollaway on Wednesday May 26, 2021 @05:21AM (#27288152)
It's not. Good quality codebases do exist, and generally are fairly well self-documented.

Typings, well-named functions and variables, consistent APIs and once in a while, a comment to summarize rationales and point out edge cases, and a README file to explain the setup and entry points.

That is generally all you need in terms of documentation. I've dealt with plenty of codebases like these and they're a delight to learn.

Re:It's probably time to stop recommending Clean Code (2020) by KaiserPro on Thursday May 27, 2021 @05:25AM (#27300893)
> they're a delight to learn

Boto3, its massive. The only reason its tolerable is that its got a large amount of accurate documentation. On the face of it, its an utter arse, because the "client" section of the library is deeply unpythonic (not in the oh its not pretty sense, as it its barely OO and uses capitals for argument names)

If that was "self documenting" I wouldn't be using it.

Take flask for example, its usable because its got good documentation and its well thought out.

but, as I keep on having to tell people. Good function names and tying is not documentation. These tell you what but not why. You don't need documentation if you are working on the same codebase day in day out. But if you have to pick it up again after 6months, you'll wish that you had properly documented it.

I get that you are proud that you don't need documentation. I don't need documentation, I want it because it makes my life 500% easier.

Re:It's probably time to stop recommending Clean Code (2020) by scrollaway on Thursday May 27, 2021 @12:40PM (#27305063)
What are you talking about? Boto3 is extremely well documented, and is absolutely not self-documenting. It's a mostly autogenerated API, and a horrible example to use for this.

I don't think you understand what my post is trying to convey. Documentation isn't a sin, it's great when necessary. In the case of boto, it's obviously necessary. In the case of Flask (a framework where you're not going to read the code), it's obviously necessary.

You'll notice, if you read through the Flask codebase, that it's only documented in terms of user-facing functions, which end up in the official end-user documentation. In that sense, the codebase itself is in fact mostly self-documenting.

There's a difference between end-user documentation and developer-facing documentation.

Re:It's probably time to stop recommending Clean Code (2020) by YeBanKo on Tuesday May 25, 2021 @08:34PM (#27284671)
> WET

One of the most difficult to argue comments in code reviews: “let’s make it generic in case we need it some place else”. First of all, chances that we need it some place else aren’t exactly high, unless you are writing a library and code explicitly design to be shared. And even if such need arises, chances of getting it right generalizing from one example are slim.

Regarding the book though, I have participated in one of the workshops with the author and he seemed to be in favor of WER and against “architecting” levels of abstraction before having concrete examples.

Re:It's probably time to stop recommending Clean Code (2020) by 29athrowaway on Tuesday May 25, 2021 @10:55AM (#27277616)
> Realize there's no such thing as "clean code"

You can disagree over what exactly is clean code. But you will learn to distinguish what dirty code is when you try to maintain it.

As a person that has had to maintain dirty code over the years, hearing someone saying dirty code doesn't exist is really frustrating. Noone wants to clean up your code, but doing it is better than allowing the code to become unmaintainable, that's why people bring up that book. If you do not care about what clean code is, stop making life difficult for people that do.

Re:It's probably time to stop recommending Clean Code (2020) by rendall on Tuesday May 25, 2021 @11:15AM (#27277901)
> hearing someone saying dirty code doesn't exist is really frustrating

Not sure why you're being downvoted, but as an unrelated aside, the quote you're responding to literally did not say this.

> that's why people bring up that book

I think the point is that following that book does not really lead to Clean Code.

Re:It's probably time to stop recommending Clean Code (2020) by 29athrowaway on Tuesday May 25, 2021 @11:31AM (#27278140)
> I think the point is that following that book does not really lead to Clean Code.

And that is why I started saying "You can disagree over what exactly is clean code". Different projects have different requirements. Working on some casual website is not the same as working on a pacemaker firmware.

Re:It's probably time to stop recommending Clean Code (2020) by rendall on Tuesday May 25, 2021 @11:58AM (#27278508)
Yeah, I don't know that we're disagreeing here. For me, there's definitely good code and bad code. I read OP more as saying perfect is the enemy of good, not that all code is good code.

I'd say if you want good code in your working environment, set up a process for requiring it. Automated testing and deployment, and code reviews would be the way to go. Reading Clean Code won't get you there.

Re:It's probably time to stop recommending Clean Code (2020) by ljm on Tuesday May 25, 2021 @11:40AM (#27278271)
I think it's more that clean code doesn't exist because there's no objective measure of this (and those services that claim there are are just as dangerous as Clean Code, the book); anyone can come along and find something about the code that could be tidied up. And legacy is legacy, it's a different problem space to the one a greenfield project exists in.

> As a person that has to maintain dirty code

This is a strange credential to present and then use as a basis to be offended. Are you saying that you have dirty code and have to keep it dirty?

Re:It's probably time to stop recommending Clean Code (2020) by 29athrowaway on Tuesday May 25, 2021 @12:05PM (#27278575)
The counterintuitive aspect of this problem that acts as a trap of the less pragmatic people, is that an objective measure is not always necessary.

Let's say you are a feeding a dog. You can estimate what amount of food is dog too little, and what amount of dog food is too much... but now, some jerk comes around and tells you they're going to feed the dog the next time. You agree.

You check the plate, and there's very little food in it. So you say: "hey, you should add more dog food".

Then, the jerk reacts by putting an excessive amount of food in it, just to fuck with you. Then you say "that's too much food!"... So then the jerk reacts saying "you should tell me exactly how many dog pellets I should put on the plate".

Have you ever had to count dog pellets individually to feed a dog? no. You haven't, you have never done it, yet you have fed dogs for years without problems just using a good enough estimate of how much a dog can eat.

Just to please the fucking jerk, you take the approximate amount dog food you regularly feed the dog every day, count the pellets, and say: "there, you fuck, 153 dog pellets".

But the jerk is not happy yet. Just to really fuck with you, the guy will challenge you and say: "so what happens if I feed the dog 152 pellets, or 154... see? you are full of shit". Then you have to explain the jerk that 153 was never important, what's important is the approximate amount of dog food. But the jerk doesn't think that way, the jerk wants a fucking exact number so they can keep fighting you...

Then the jerk will probably say that a dog pellet is not a proper unit of mass, and then the jerk will say that nutrients are not equally distributed in dog pellets, and the bullshit will go on and on and on.

And if you are ever done discussing the optimal number of pellets then there will be another discussion about the frequency in which you feed the dog, and you will probably end up talking about spacetime and atomic clocks and the NTP protocol and relativity and inertial frames just to please the jerk whose objective is just to waste your time until you give up trying to enforce good practices.

And this is how the anti-maintainability jerks operate, by getting into endless debates about how an objective measure of things is required, when in reality, it's not fucking needed and it never was. Estimation is fine.

Just like you won't feed a dog a fucking barrel of dog food you won't create a function that is 5 thousand lines of code long because it's equally nonsensical.

So in the end, what do you do? you say: this many lines of code in a function is too fucking much, don't write functions this long. Why? because I say so, end of debate, fuck it.

There's a formal concept for this in philosophy, but that's up to you to figure out.

Re:It's probably time to stop recommending Clean Code (2020) by mattgreenrocks on Tuesday May 25, 2021 @09:09PM (#27284902)
This is why I never talk software architecture with anyone on the internet anymore. It’s always an intellectual race to the bottom with someone who insists on a rigid process for things that are absolutely obvious to anyone with experience and genuine concern for the state of the codebase.
Re:It's probably time to stop recommending Clean Code (2020) by Twisol on Tuesday May 25, 2021 @12:49PM (#27279183)
> There's a formal concept for this in philosophy, but that's up to you to figure out.

I think I know this one! Is it the paradox of the heap?

https://en.wikipedia.org/wiki/Sorites_paradox

Re:It's probably time to stop recommending Clean Code (2020) by 29athrowaway on Tuesday May 25, 2021 @12:51PM (#27279214)
Exactly.
Re:It's probably time to stop recommending Clean Code (2020) by Chris2048 on Tuesday May 25, 2021 @12:56PM (#27279281)
Another way to say (I think) the same is:

The cost of analysis outweighs the benefit.

In this case the cost of counting food pellets versus the benefit of precision; but in software generally it is the high cost of (accurate) estimating versus the benefit of just getting on with it.

It is also, to some degree, the impossibility of estimating given that the task is given to someone who is an expert at writing code, not estimating. Coding expertise give insight into how long coding tasks take, but that it the least critical component of estimating a task. Writing code and seeing if it works is the best estimate; sometimes the prototype works first time, never always.

best example of this: https://xkcd.com/1445/

Re:It's probably time to stop recommending Clean Code (2020) by ljm on Tuesday May 25, 2021 @01:59PM (#27280079)
> Just to please the fucking jerk

That's probably mistake number one right there.

There's another formal concept for this in philosophy, related to wrestling with pigs.

Re:It's probably time to stop recommending Clean Code (2020) by 29athrowaway on Tuesday May 25, 2021 @02:01PM (#27280113)
Just trying to illustrate why it does not make sense to even get started down that path and the conclusion reflects this.
Re:It's probably time to stop recommending Clean Code (2020) by ljm on Tuesday May 25, 2021 @02:34PM (#27280514)
I agree.

Despite approaching this from completely different angles I think we're roughly pointing in the same direction.

A pragmatist would allow for a strong baseline of good practices and automated rules, with some room for discretion where it counts. That way, no one gets bogged down in exhausting debates, and people who work better with strict constraints can avoid ambiguity, but you can still bend the rules with some justification. I don't like all of the rules, but it's easy to fall back on the tooling than it is to hash out another method length debate.

As far as maintaining code goes, I've had the (mis)fortune of dealing with overly abstracted messes and un-architected spaghetti. I'm not sure which is worse, but what I rarely have to deal with now is all the crap that is auto-formatted away.

If I'm involved in any debate, it's around architecture and figuring out how to write better code by properly understanding what we're trying to achieve, rather than trying to lay down Clean Code style design patterns as if they were Rubocop rules.

Re:It's probably time to stop recommending Clean Code (2020) by 29athrowaway on Tuesday May 25, 2021 @03:19PM (#27281226)
Well, if there is a problem, you can identify it, discuss it and address it, instead of ignoring it entirely which is what some people do. Do what works for your team.

Excessive abstractions, leaky abstractions and such are indeed a problem. The principle or least power works there (given a choice of solutions, use the least flexible/powerful to solve your problem).

Re:It's probably time to stop recommending Clean Code (2020) by Cthulhu_ on Tuesday May 25, 2021 @11:33AM (#27278187)
This is what I'm doing even while creating new code. There's a few instances for example where the "execution" is down to a single argument - one of "activate", "reactivate" and "deactivate". But I've made them into three distinct, separate code paths so that I can work error and feedback messages into everything without adding complexity via arguments.

I mean yes it's more verbose, BUT it's also super clear and obvious what things do, and they do not leak the underlying implementation.

Re:It's probably time to stop recommending Clean Code (2020) by bosswipe on Tuesday May 25, 2021 @12:30PM (#27278922)
Your name WET is kind of funny but it is usually refered to as the rule of 3, https://en.m.wikipedia.org/wiki/Rule_of_three_(computer_prog...
Re:It's probably time to stop recommending Clean Code (2020) by bengale on Tuesday May 25, 2021 @04:57PM (#27282548)
I’ve never heard the term WET before but that’s exactly what I do.

The other key thing I think is not to over-engineer abstractions you don’t need yet. But to try and leave ‘seams’ where it’s obvious how to tease code about if you need to start building abstractions.

Re:It's probably time to stop recommending Clean Code (2020) by heisenbit on Wednesday May 26, 2021 @03:51AM (#27287616)
My experience interviewing recently a number of consultants with only a few years experience was the more they mumbled clean code the less they knew what they were doing.
Re:It's probably time to stop recommending Clean Code (2020) by thefourthchime on Tuesday May 25, 2021 @11:07AM (#27277795)
Ah hahha. I love WET. I always say the only way to write something correctly is to re-write it.
Re:It's probably time to stop recommending Clean Code (2020) by HWR_14 on Tuesday May 25, 2021 @11:30AM (#27278130)
That's not what WET means. The GP is saying you shouldn't isolate logic in a function until you've cut-and-pasted the logic in at least two places and plan to do so in a third.
Re:It's probably time to stop recommending Clean Code (2020) by TeMPOraL on Tuesday May 25, 2021 @11:50AM (#27278396)
> Put logic closest to where it needs to live (feature folders)

Can you say more about this?

I think I may have stumbled on a similar insight myself. In a side project (a roguelike game), I've been experimenting with a design that treats features as first-class, composable design units. Here is a list of the subfolder called game-features in the source tree:

  actions
  collision
  control
  death
  destructibility
  game-feature.lisp
  hearing
  kinematics
  log
  lore
  package.lisp
  rendering
  sight
  simulation
  transform
An extract from the docstring of the entire game-feature package:

  "A Game Feature is responsible for providing components, events,
  event handlers, queries and other utilities implementing a given
  aspect of the game. It's primarily a organization tool for gameplay code.
  
  Each individual Game Feature is represented by a class inheriting
  from `SAAT/GF:GAME-FEATURE'. To make use of a Game Feature,
  an object of such class should be created, preferably in a
  system description (see `SAAT/DI').
  This way, all rules of the game are determined by a collection of
  Game Features loaded in a given game.
  
  Game Features may depend on other Game Features; this is represented
  through dependencies of their classes."
The project is still very much work-in-progress (procrastinating on HN doesn't leave me much time to work on it), and most of the above features are nowhere near completion, but I found the design to be mostly sound. Each game feature provides code that implements its own concerns, and exports various functions and data structures for other game features to use. This is an inversion of traditional design, and is more similar to the ECS pattern, except I bucket all conceptually related things in one place. ECS Components and Systems, utility code, event definitions, etc. that implement a single conceptual game aspect live in the same folder. Inter-feature dependencies are made explicit, and game "superstructure" is designed to allow GFs to wire themselves into appropriate places in the event loop, datastore, etc. - so in game startup code, I just declare which features I want to have enabled.

(Each feature also gets its set of integration tests that use synthetic scenarios to verify a particular aspect of the game works as I want it to.)

One negative side effect of this design is that the execution order of handlers for any given event is hard to determine from code. That's because, to have game features easily compose, GFs can request particular ordering themselves (e.g. "death" can demand its event handler to be executed after "destructibility" but before "log") - so at startup, I get an ordering preference graph that I reconcile and linearize (via topological sorting). I work around this and related issues by adding debug utilities - e.g. some extra code that can, after game startup, generate a PlantUML/GraphViz picture of all events, event handlers, and their ordering.

(I apologize for a long comment, it's a bit of work I always wanted to talk about with someone, but never got around to. The source of the game isn't public right now because I'm afraid of airing my hot garbage code.)

Re:It's probably time to stop recommending Clean Code (2020) by Chris2048 on Tuesday May 25, 2021 @01:24PM (#27279648)
I'd be interested in how you attempt this. Is it all in lisp?

It might be hard to integrate related things, e.g. physical simulation/kinematics <- related to collisions, and maybe sight/hearing <- related to rendering; Which is all great if information flows one way, as a tree, but maybe complicated if it's a graph with intercommunication.

I thought about this before, and figured maybe the design could be initially very loose (and inefficient), but then a constraint-solver could wire things up as needed, i.e. pre-calculate concerns/dependencies.

Another idea, since you mention "logs" as a GF: AOP - using " join points" to declaratively annotate code. This better handles code that is less of a "module" (appropriate for functions and libraries) and more of a cross-cutting "aspect" like logging. This can also get hairy though: could you treated "(bad-path) exception handling" as an aspect? what about "security"?

Re:It's probably time to stop recommending Clean Code (2020) by TeMPOraL on Tuesday May 25, 2021 @02:27PM (#27280419)
> I'd be interested in how you attempt this. Is it all in lisp?

Yes, it's all in Common Lisp.

> It might be hard to integrate related things, e.g. physical simulation/kinematics <- related to collisions, and maybe sight/hearing <- related to rendering;

It is, and I'm cheating a bit here. One simplification is that I'm writing a primarily text-based roguelike, so I don't have to bother with a lot of issues common to real-time 3D games. I can pick and choose the level of details I want to go (e.g. whether to handle collisions at a tile granularity, or to introduce sub-tile coordinates and maybe even some kind of object shape representation).

> Which is all great if information flows one way, as a tree, but maybe complicated if it's a graph with intercommunication.

The overall simulation architecture I'm exploring in this project is strongly event-based. The "game frame" is basically pulling events from a queue and executing them until the queue is empty, at which point the frame is considered concluded, and simulation time advances forward. It doesn't use a fixed time step - instead, when a simulation frame starts, the code looks through "actions" scheduled for game "actors" to find the one that will complete soonest, and moves the simulation clock to the completion time of that action. Then the action completion handler fires, which is the proper start of frame - completion handler will queue some events, and handlers of those events will queue those events, and the code just keeps processing events until the queue empties again, completing the simulation frame.

Structure-wise, simulation GF defines the concept of "start frame" and "end frame" (as events), "game clock" (as query) and ability to shift it (as event handler), but it's the actions GF that contains the computation of next action time. So, simulation GF knows how to tell and move time, but actions GF tells it where to move it to.

This is all supported by an overcomplicated event loop that lets GFs provide hints for handler ordering, but also separates each event handling process into four chains: pre-commit, commit, post-commit and abort. Pre-commit handlers fire first, filling event structure with data and performing validation. Then, commit handlers apply direct consequences of event to the real world - they alter the gameplay state. Then, post-commit handlers process further consequences of an event "actually happening". Alternatively, abort handlers process situations when an event was rejected during earlier chains. All of them can enqueue further events to be processed this frame.

So, for example, when you fire a gun, pre-commit handlers will ensure you're able to do it, and reject the event if you can't. If the event is rejected, abort chain will handle informing you that you failed to fire. Otherwise, the commit handlers will instantiate an appropriate projectile. Post-commit handlers may spawn events related to the weapon being fired, such as informing nearby enemies about the discharge.

This means that e.g. if I want to implement "ammunition" feature, I can make an appropriate GF that attaches a pre-commit handler to fire event - checking if you have bullets left and rejecting the event if you don't (events rejected in pre-commit stage are considered to "have never happened"), and a post-commit handler on the same event to decrease your ammo count. The GF is also responsible for defining appropriate components that store ammo count, so that (in classical ECS style) your "gun" entity can use it to keep track of ammunition. It also provides code for querying the current count, for other GFs that may care about it for some reason (and the UI rendering code).

> I thought about this before, and figured maybe the design could be initially very loose (and inefficient), but then a constraint-solver could wire things up as needed, i.e. pre-calculate concerns/dependencies.

I'm halfway there and I could easily do this, but for now opted against it, on the "premature optimization" grounds. That is, since all event handlers are registered when the actual game starts, I "resolve constraints" (read: convert sorting preferences into a DAG and toposort it; it's dumb and very much incorrect, but works well enough for now) and linearize handlers - so that during gameplay, each event handler chain (e.g. "fire weapon", pre-commit) is just a sequence of callbacks executed in order. It would be trivial to take such sequence, generate a function that executes them one by one (with the loop unrolled), and compile it down to metal - Common Lisp lets you do stuff like that - but I don't need it right now.

> Another idea, since you mention "logs" as a GF

FWIW, logs GF is implementing the user-facing logs typical for roguelike games - i.e. the bit that says "You open the big steel doors". Diagnostic logs I do classically.

> AOP - using " join points" to declaratively annotate code

In a way, my weird multi-chain event loop is a reimplementation of AOP. Method combinations in Common Lisp are conceptually similar too, but I'm not making big use of them in game feature-related code.

> This can also get hairy though: could you treated "(bad-path) exception handling" as an aspect? what about "security"?

Yeah, I'm not sure if this pattern would work for these - particularly in full-AOP, "inject anything anywhere" mode. I haven't tried it. Perhaps, with adequate tooling support, it's workable? Common Lisp is definitely not a language to try this in, though - it's too dynamic, so tooling would not be able to reliably tell you about arbitrary pointcuts.

In my case, I restricted the "feature-oriented design" to just game features - I feel it has a chance of working out, because in my mind, quite a lot of gameplay mechanics are conceptually additive. This project is my attempt at experimentally verifying if one could actually make a working game this way.

Re:It's probably time to stop recommending Clean Code (2020) by mike_ivanov on Tuesday May 25, 2021 @12:16PM (#27278727)
Sounds interesting. Is the game open source? Published anywhere?
Re:It's probably time to stop recommending Clean Code (2020) by TeMPOraL on Tuesday May 25, 2021 @12:55PM (#27279267)
Not at this moment. I intend to publish it under GPLv3, after I get the MVP done.
Re:It's probably time to stop recommending Clean Code (2020) by lxdesk on Tuesday May 25, 2021 @02:21PM (#27280339)
I've gone down roads similar to this. Long story short - the architecture solves for a lower priority class of problem, w/r to games, so it doesn't pay a great dividend, and you add a combination of boilerplate and dynamism that slows down development.

Your top issue in the runtime game loop is always with concurrency and synchronization logic - e.g. A spawns before B, if A's hitbox overlaps with B, is the first frame that a collision event occurs the frame of spawning or one frame after? That's the kind of issue that is hard to catch, occurs not often, and often has some kind of catastrophic impact if handled wrongly. But the actual effect of the event is usually a one-liner like "set a stun timer" - there is nothing to test with respect to the event itself! The perceived behavior is intimately coupled to when its processing occurs and when the effects are "felt" elsewhere in the loop - everything's tied to some kind of clock, whether it's the CPU clock, the rendered frame, turn-taking, or an abstracted timer. These kinds of bugs are a matter of bad specification, rather than bad implementation, so they resist automated testing mightily.

The most straightforward solution is, failing pure functions, to write more inline code(there is a John Carmack posting on inline code that I often use as a reference point). Enforce a static order of events as often as possible. Then debugging is always a matter of "does A happen before B?" It's there in the source code, and you don't need tooling to spot the issue.

The other part of this is, how do you load and initialize the scene? And that's a data problem that does call for more complex dependency management - but again, most games will aim to solve it statically in the build process of the game's assets, and reduce the amount of game state being serialized to save games, reducing the complexity surface of everything related to saves(versioning, corruption, etc). With a roguelike there is more of an impetus to build a lot of dynamic assets(dungeon maps, item placements etc.) which leads to a larger serialization footprint. But ultimately the focus of all of this is on getting the data to a place where you can bring it back up and run queries on it, and that's the kind of thing where you could theoretically use SQLite and have a very flexible runtime data model with a robust query system - but fully exploiting it wouldn't have the level of performance that's expected for a game.

Now, where can your system make sense? Where the game loop is actually dynamic in its function - i.e. modding APIs. But this tends to be a thing you approach gradually and grudgingly, because modders aren't any better at solving concurrency bugs and they are less incentivized to play nice with other mods, so they will always default to hacking in something that stomps the state, creating intermittent race conditions. So in practice you are likely to just have specific feature points where an API can exist(e.g. add a new "on hit" behavior that conditionally changes the one-liner), and those might impose some generalized concurrency logic.

The other thing that might help is to have a language that actually understands that you want to do this decoupling and has the tooling built in to do constraint logic programming and enforce the "musts" and "cannots" at source level. I don't know of a language that really addresses this well for the use case of game loops - it entails having a whole general-purpose language already and then also this other feature. Big project.

I've been taking the approach instead of aiming to develop "little languages" that compose well for certain kinds of features - e.g. instead of programming a finite state machine by hand for each type of NPC, devise a subcategory of state machines that I could describe as a one-liner, with chunks of fixed-function behavior and a bit of programmability. Instead of a universal graphics system, have various programmable painter systems that can manipulate cursors or selections to describe an image. The concurrency stays mostly static, but the little languages drive the dynamic behavior, and because they are small, they are easy to provide some tooling for.

Re:It's probably time to stop recommending Clean Code (2020) by TeMPOraL on Tuesday May 25, 2021 @05:39PM (#27283055)
Thanks for the detailed evaluation. I'll start by reiterating that the project is a typical tile-based roguelike, so some of the concerns you mention in the second paragraph don't apply. Everything runs sequentially and deterministically - though the actual order of execution may not be apparent from the code itself. I mitigate it to an extent by adding introspection features, like e.g. code that dumps PlantUML graphs showing the actual order of execution of event handlers, or their relationship with events (e.g. which handlers can send what subsequent events).

I'll also add that this is an experimental hobby project, used to explore various programming techniques and architecture ideas, so I don't care about most constraints under which commercial game studios operate.

> The perceived behavior is intimately coupled to when its processing occurs and when the effects are "felt" elsewhere in the loop - everything's tied to some kind of clock, whether it's the CPU clock, the rendered frame, turn-taking, or an abstracted timer. These kinds of bugs are a matter of bad specification, rather than bad implementation, so they resist automated testing mightily.

Since day one of the project, the core feature was to be able to run headless automated gameplay tests. That is, input and output are isolated by design. Every "game feature" (GF) I develop comes with automated tests; each such test starts up a minimal game core with fake (or null) input and output, the GF under test, and all GFs on which it depends, and then executes faked scenarios. So far, at least for minor things, it works out OK. I expect I might hit a wall when there are enough interacting GFs that I won't be able to correctly map desired scenarios to actual event execution orders. We'll see what happens when I reach that point.

> that's the kind of thing where you could theoretically use SQLite and have a very flexible runtime data model with a robust query system - but fully exploiting it wouldn't have the level of performance that's expected for a game.

Funny you should mention that.

The other big weird thing about this project is that it uses SQLite for runtime game state. That is, entities are database rows, components are database tables, and the canonical gameplay state at any given point is stored in an in-memory SQLite database. This makes saving/loading a non-issue - I just use SQLite's Backup API to dump the game state to disk, and then read it back.

Performance-wise, I tested this approach extensively up front, by timing artificial reads and writes in expected patterns, including simulating a situation in which I pull map and entities data in a given range to render them on screen. SQLite turned out to be much faster than I expected. On my machine, I could easily get 60FPS out of that with minimum optimization work - but it did consume most of the frame time. Given that I'm writing a ASCII-style, turn(ish) roguelike, I don't actually need to query all that data 60 times per second, so this is quite acceptable performance - but I wouldn't try that with a real-time game.

> The other thing that might help is to have a language that actually understands that you want to do this decoupling and has the tooling built in to do constraint logic programming and enforce the "musts" and "cannots" at source level. I don't know of a language that really addresses this well for the use case of game loops - it entails having a whole general-purpose language already and then also this other feature. Big project.

Or a Lisp project. While I currently do constraint resolution at runtime, it's not hard to move it to compile time. I just didn't bother with it yet. Nice thing about Common Lisp is that the distinction between "compilation/loading" and "runtime" is somewhat arbitrary - any code I can execute in the latter, I can execute in the former. If I have a function that resolves constraints on some data structure and returns a sequence, and that data structure can be completely known at compile time, it's trivial to have the function execute during compilation instead.

> I've been taking the approach instead of aiming to develop "little languages" that compose well for certain kinds of features

I'm interested in learning more about the languages you developed - e.g. how your FSMs are encoded, and what that "programmable painter system" looks like. In my project, I do little languages too (in fact, the aforementioned "game features" are a DSL themselves) - Lisp makes it very easy to just create new DSLs on the fly, and to some extent they inherit the tooling used to power the "host" language.

Re:It's probably time to stop recommending Clean Code (2020) by lxdesk on Wednesday May 26, 2021 @02:02AM (#27286917)
Sounds like you may be getting close to an ideal result, at least for this project! :) Nice on the use of SQLite - I agree that it's right in the ballpark of usability if you're just occasionally editing or doing simple turn-taking.

When you create gameplay tests, one of the major limitations is in testing data. Many games end up with "playground" levels that validate the major game mechanics because they have no easier way of specifying what is, in essence, a data bug like "jump height is too short to cross gap". Now, of course you can engineer some kind of test, but it starts to become either a reiteration of the data (useless) or an AI programming problem that could be inverted into "give me the set of values that have solutions fitting these constraints" (which then isn't really a "test" but a redefinition of the medium, in the same way that a procedural level is a "solution" for a valid level).

It's this latter point that forms the basis of many of the "little languages". If you hardcode the constraints, then more of the data resides in a sweet spot by default and the runtime is dealing with less generality, so it also becomes easier to validate. One of my favorite examples of this is the light style language in Quake 1: https://quakewiki.org/wiki/lightstyle

It's just a short character string that sequences some brightness changes in a linear scale at a fixed rate. So it's "data," but it's not data encoded in something bulky like a bunch of floating point values. It's of precisely the granularity demanded by the problem, and much easier to edit as a result.

A short step up from that is something like MML: https://en.wikipedia.org/wiki/Music_Macro_Language - now there is a mostly-trivial parsing step involved, but again, it's "to the point" - it assumes features around scale and rhythm that allow it to be compact. You can actually do better than MML by encoding an assumption of "playing in key" and "key change" - then you can barf nearly any sequence of scale degrees into the keyboard and it'll be inoffensive, if not great music. Likewise, you could define rhythm in terms of rhythmic textures over time - sparse, held, arpeggiated, etc. - and so not really have to define the music note by note, making it easy to add new arrangements.

With AI, a similar thing can apply - define a tighter structure and the simpler thing falls out. A lot of game AI FSMs will follow a general pattern of "run this sequenced behavior unless something of a higher priority interrupts it". So encode the sequence, then hardcode the interruption modes, then figure out if they need to be parameterized into e.g. multiple sequences, if they need to retain a memory scratchpad and resume, etc. A lot of the headache of generalizing AI is in discovering needs for new scratchpads, if just to do something like a cooldown timer on a behavior or to retain a target destination. It means that your memory allocation per entity is dependent on how smart they have to be, which depends on the AI's program. It's not so bad if you are in something as dynamic as a Lisp, but problematic in the typical usages of ECS where part of the point is to systematize memory allocation.

With painting what you're looking for is a structuring metaphor for classes of images. Most systems of illustration have structuring metaphors of some kind specifically for defining proportions - they start with simple ratios and primitive shapes, and then use those as the construction lines for more detailed elements which subdivide the shapes again with another set of ratios. This is the conceptual basis of the common "6-8 heads of height" tip used in figure drawing - and there are systems of figure drawing which get really specific about what shapes to draw and how. If I encode such a system, I therefore have a method of automatic illustration that starts not with the actual "drawing" of anything, but with a proportion specification creating construction lines, which are then an input to a styling system that defines how to connect the lines or superimpose other shapes. Something I've been experimenting with to get those lines is a system that works by interpolation of coordinate transforms that aggregate a Cartesian and polar system together - e.g. I want to say "interpolate along this Cartesian grid, after it's been rotated 45 degrees". It can also perform interpolation between two entirely different coordinate systems(e.g. the same grid at two different scales). I haven't touched it in a while, but it generates interesting abstract animations, and I have a vision for turning that into a system for specifying character mannequins, textures, etc. Right now it's too complex to be a good one-liner system, but I could get there by building tighter abstractions on it in the same way as the music system.

My main thing this year has been a binary format that lets me break away from text encodings as the base medium, and instead have more precise, richer data types as the base cell type. This has gone through a lot of iteration to test various things I might want to encode and forms I could encode them in. The key thing I've hit on is to encode with a lot of "slack" in the system - each "cell" of data is 16 bytes; half of that is a header that contains information about how to render it, its ID in a listing of user named types, bitflags defined by the type, a "feature" value(an enumeration defined by the type), and a version field which could be used for various editing features. The other half is a value, which could be a 64-bit value, 8 bytes, a string fragment, etc. - the rendering information field indicates what it is in those general terms, but the definite meaning is named by the user type. The goal is to use this as a groundwork to define the little languages further - rather than relying on "just text" and sophisticated parsing, the parse is trivialized by being able to define richer symbols - and then I can provide more sophisticated editing and visualization more easily. Of course, I'm placing a bet on either having an general-purpose editor for it that's worthwhile, or being able to define custom editors that trivialize editing, neither of which might pan out; there's a case for either "just text" or "just spreadsheets" still beating my system. But I'd like to try it, since I think this way of structuring the bits is likely to be more long-run sustainable.

Re:It's probably time to stop recommending Clean Code (2020) by TeMPOraL on Wednesday May 26, 2021 @06:50PM (#27297134)
Thanks for the detailed explanation!

I don't think I can respond properly and ask the many more questions I have in a HN thread that's already this aged, so if you'd like to talk more, feel free to hit me up (my contact details are in my profile).