787 comments

[ 3.0 ms ] story [ 503 ms ] thread
> Java is a great language because it's boring [...] Types are assertions we make about the world

This is less of a mind-was-changed case and more just controversial, but... Checked Exceptions were a fundamentally good idea. They just needed some syntactic sugar to help redirect certain developers into less self-destructive ways of procrastinating on proper error handling.

In brief for non-Java folks: Checked Exceptions are a subset of all Exceptions. To throw them, they must be part of the function's type signature. To call that function, the caller code must make some kind of decision about what to do when that Checked Exception arrives. [0] It's basically another return type for the method, married with the conventions and flow-control features of Exceptions.

[0] Ex: Let it bubble up unimpeded, adding it to your own function signature; catch it and wrap it in your own exception with a type more appropriate to the layer of abstraction; catch it and log it; catch it and ignore it... Alas, many caught it and wrapped it in a generic RuntimeException.

remind me why they don’t work? because “throws Exception” propagates virally to every method in the codebase?
Well, if you don’t handle them then… yes? What else can be done?
There's also the fact that common methods threw exception types that were not final, and in fact overly generic. If I call a method that declares itself to throw NoSuchFileException or DirectoryNotEmptyException, I can have a pretty good idea what I might do about it. If it throws IOException without elaboration, on the other hand...
With regards to I/O, there are generally any number of weird errors you can run into, file not found, directory exists, host unreachable, permission denied, file corrupt, etc etc. Like anything file related will be able to throw any of those exceptions at almost any point in time.

I think IOException (or maybe FileSystemException) is probably the best you can do in a lot of I/O cases unless you can dedicate time to handling each of those specially (and there's often not a lot much more you can do except for saying "Access is denied" or "File not found" to the user or by logging it somewhere).

There are fatal IO errors and non-fatal. Most of us don't care about the non-fatal case, but mainframes have the concept of "file isn't available now, load tape 12345", "file is being restored from backup, try again tomorrow" - things that your code could handle (if nothing else you should inform the user that this will take a while). There is also the "read off the end of the file" exception which in some languages is the idiomatic way to tell when you have read the whole file.

But most IO errors are fatal. It doesn't matter if the filename is not found, or the controller had too many errors talking to the drive and gave up - either way your code can do nothing.

Because you can't capture the evaluation of a function as a value, or write the type of it. E.g. try to write a generic function that takes a list and a callback, and applies the callback to every element of the list. Now what happens if your callback throws a checked exception? It doesn't work and there's no way to make it work, you just have to write another overload of your function and copy/paste your code. Now what happens if your callback throws two checked exceptions? It doesn't work and there's no way to make it work, you just have to write another overload of your function and copy/paste your code. And you'll never guess what happens if your callback throws three checked exceptions!
what is "it doesn't work" ? The exception is part of the type, so it doesn't typecheck unless all callbacks are of type "... throws Exception"? What's the problem with that? It's not generic enough, i.e. the problem is Java generics are too weak to write something like "throws <T extends Exception>"? (Forgive me, it's been 13 years since I wrote java and only briefly, the questions are earnest)

edit, so like `@throws[T <: Exception] def effect[T](): Unit` or something, how is it supposed to work?

You can't be polymorphic between not throwing and throwing, or between throwing different numbers of exceptions. You have to write something like:

    <R> List<R> map(Function<? super T,? extends R> mapper) { ... }
    <R, E1 extends Throwable> List<R> map(FunctionThrows1<? super T,? extends R, E1> mapper) throws E1 { ... }
    <R, E1 extends Throwable, E2 extends Throwable> List<R> map(FunctionThrows2<? super T,? extends R, E1, E2> mapper) throws E1, E2 { ... }
and so on until you get bored.
Ah because there is no way to express E1 | E2 as a type parameter?
Yeah. Ironically the JLS includes a complete specification of what the type E1|E2 is, because if you write a catch block that catches both then that's the type of what you catch, there's just no syntax for it.
Make the signature of your generic callback "throws Throwable". It's generic; it should never care about the specific types that the callback can throw.

(Except that then you have to decide what your generic function is going to do if the callback throws an exception...)

> Make the signature of your generic callback "throws Throwable". It's generic; it should never care about the specific types that the callback can throw.

> (Except that then you have to decide what your generic function is going to do if the callback throws an exception...)

Exactly. Presumably you don't want to handle them and want to throw them up to the caller. But now your function has to be "throws Throwable" rather than throwing the specific exception types that the callback throws.

By doing that you lose all the benefits of checked exceptions. If you have checked exceptions everywhere the compiler will tell you when an exception is not handled and in turn you can ensure you handle it.

Of course in general the manual effort to do that in a large code base ends up too hard and so in the real world nobody does that. Still the ideal is good, just the implementation is flawed.

In that case you can use the lombok @SneakyThrows annotation that converts a checked exception to a runtime exception
Because eventually someone will want to add another exception to a new leaf class and the proper place to handle it is 20 functions down the call tree and every single one of those 20 functions needs to now add that new exception to their signature even though only the handler function cares and you adjusted it.

I haven't done java in decades, but I imagine this would get really nasty if you are passing a callback to a third party library and now your callback throws the new exception.

Checked exceptions seem like a great idea, but what java did is wrong. I'm not sure if other implementations are better.

The counter point here is that at least with Checked Exceptions you know only those 20 functions are part of the code path that can throw that exception. In the runtime exception case you are unaware about that 21'st function elsewhere that now throws it and it's not in the correct handling path anymore.

You have no way to assert that the error is always correctly handled in the codebase. You are basically crossing your fingers and hoping that over the life of the codebase you don't break the invariant.

What was missing was a good way to convert checked exceptions from code you don't own into your own error domain. So instead java devs just avoided them because it was more work to do the proper error domain modeling.

Like I said, the implementation is wrong. Adding an exception to that 21st function, and then that whole call chain as well ends up being a lot of work. Sure you eventually find the place to handle it, but it was a lot of effort in the mean time.

It gets worse. Sometimes we can prove that the 21st function because of the way it is calling your function can never trigger that exception, but still it will need code to handle it. If that handler code if the 21st function changes to trigger the exception now should be propagated back down but since you handled the exception before checked exceptions won't tell you that you handled it in the wrong place.

I don't know how to implement checked exceptions right. On paper they have a lot of great arguments for them. However in practice they don't work well in large projects (at least for java)

The Rust Result type with accompanying `?` operator and the `Try*` traits are how you implement exceptions correctly. It makes it easy to model the error domain once in your trait implementations and then the `?` does the rest of the work.

You could imagine something similar with Exceptions where there is simple and ergonomic way to rethrow the exception into your own error domain with little to no extra work on the part of the developer.

It was botched from the start because there's so many opportunities for unchecked exceptions as well. Without a more sophisticated type system that represented nullability, you can get NullPointerException anywhere. Divide by zero. And so on.

You also have a problem similar to "monads and logging": if you want to log from anywhere in your program, your logging function needs to be exception-tight and deal with all the possible problems such as running out of disk space, otherwise you have to add those everywhere.

Unchecked exceptions are just Java's weird way of what languages call panicking these days. They suck, but as long as you don't throw them yourself and catch them in a logical place (i.e. at request level so your web server doesn't die, at queue level so your data processing management doesn't lock up, etc.) you can usually pretty much ignore them.

The worst part about them is that for some reason even standard library methods will throw them. Like when you try `list.Add(1)` on a list without checking if said list is read-only. The overhead of having to read every single bit of documentation in the standard library just to be ahead of panicking standard methods is infuriating.

That's got nothing to do with the concept of checked/unchecked exceptions, though, that's just Java's mediocre standard library.

(comment deleted)
> Without a more sophisticated type system that represented nullability, you can get NullPointerException anywhere.

I started working in Java a few months ago and holy shit does this stick out like a sore thumb. Null checks cascade down from gods domain all the way to hell. But oop we missed one here and caused an outage lol add one more! So much wasted human effort around NPE, and yet, we sit around in a weekly meeting getting yelled at about how stability needs to be taken more seriously. Hrm.

C# half-fixes this with its nullable annotations. I say half-fixes, because the boundary between code that supports them and code that does not is leaky, so you can make a mistake that leaks a null into a non-nullable variable.

If you build an entire program with nullability checking on it's pretty great, though.

Java, or at least Lombok, seems to have a @NonNull annotation that does what I want— cause code not to build that fails the check, and forces propagation of the annotation.

Reality does indeed feel exactly like what you mentioned with C#, though. The annotation is going to be missing where it’s needed most unless something forces the whole project to use it.

check JSpecify (https://jspecify.dev) - it's the standardised null annotation package for Java. Intellij understands the annotations so you generally get decent null-checking across your codebase.

Even better, apply at the package level via `package-info.java` (unfortunately sub-packages need to be individually marked as well)

  @NullMarked
  package com.foo;

  import org.jspecify.annotations.NullMarked;
Kotlin fixes the null handling problem too, and with the added benefit of being able to gradually migrate Java code.
The problem there was really that Java confused unrecoverable errors with recoverable errors. NPEs and divide by zero should make the program abort (possibly with another, completely different mechanism to catch these if you really want to, a la Rust's panic handlers).

Recoverable errors should all be checked exceptions, and a part of each function's type signature. This would still be a huge pain to deal with, though, with the existing syntax.

> Alas, many caught it and wrapped it in a generic RuntimeException.

Actually sounded great right up until this point. Deal with it, or explicitly acknowledge that you do not. It's honest.

Apparently other developers are why we can't have nice things.

It's already very unlikely that you can 'recover-and-proceed' in the context of any business app exception (Security violation, customer not found, no such payment, etc.).

So what's left in exception handling is logging and/or rethrowing. And the 'nasty hackish way' of doing it (RuntimeException) already passes a complete stack trace up to the caller.

"recover and proceed" generally means "log the error and then continue processing on other data, rather than exiting entirely"

Lots of different kinds of software tends to follow this pattern - web servers, data pipelines, compilers/build tools, etc.

I agree, although I would like to point out Java usually gets the blame for what was actually an idea being done in CLU, Mesa, Modula-3 and C++, before Oak came to be and turned into Java.

Additionally, the way result types work, isn't much different, from type system theory point of view.

I really miss them in .NET projects, because no one reads method documentation, or bothers to have catch all clauses, and then little fellow crashes in production.

Anti checked exception sentiment is the result of cognitive dissonance from the use of flow-of-control obfuscation frameworks.

IoC, DI, aspects, annotations, ORMs, whatever Spring is, runtime code generation, etc.

The rationale is something like "runtime metaprogramming would be really terrific, were it not for all those pesky checked exceptions".

Checked exceptions as an idea are great (Nim’s usage of something similar is excellent) but yeah Javas particular implementation was annoying and easy to avoid, so most did.
> adding it to your own function signature

This is precisely why they are so bad: checked exceptions must not be allowed to be used outside the package (or jar, or whatever, just limit it) otherwise they cause non-local build failures in all dependencies. They're fine if you are developing the artifact that's going to be deployed.

I think that’s more of a problem of changing the function signature. Not specific to checked exceptions.
What's specific in checked exceptions is that if you don't handle or silently ignore the new exception, you must change the signature. Then your callers must do the same thing. Then their callers etc. sometimes right down to your public static void main.
Yep, don’t change the function signature of depended on code. That’s the problem :)

It would be equally problematic to change the method name, or the argument arity or types (ignoring overloading for the moment).

It would be even more problematic if I kept the same function signature, but changed the meaning of the parameters. Then your breakage is silent.

yes exactly, hence unchecked exceptions are the sane option.
Doesn’t that just introduce a silent breaking change to your downstream consumers? That sounds worse.

It sounds ok from a library authors perspective but definitely not from a library consumer perspective.

A sane thing to do would be to do a version bump.

consider the standard library case - e.g. there's a new kind of exception because new storage or network technology demands it. you can't add it without breaking the build of everything everywhere effectively freezing the standard library version for people who don't have the means to fix their build. that's super duper bad.
1. The standard library is special in many ways, particularly because it often isn't shipped along with your product and you can't always control what version is used. Just because something is problematic for those libraries doesn't mean it it's a bad idea everywhere else.

2. The difference between altering your un/checked exceptions is not whether consumers will have to react, but how it shows up and how badly you will ruin their day. A checked exception is unambiguously better. It will immediately break their build at the same time they ought to be expecting build-breaks, and the compiler will give them a clear and comprehensive list of cases to address. In contrast, an unchecked exception may let them compile but it will break their business in production, unpredictably.

Re 1) when the standard library becomes a major blocker for the runtime version upgrade many people are seriously angry, or depressed.

Re 2) that's what it must've sounded like in theory in the conference room when they were designing that part of the language. In practice, the upgrade of the library will never happen if it breaks the build. Production should catch all runtime exceptions and be able to restart itself gracefully anyway because cosmic rays don't make sense as checked exceptions.

And that is extremely good compared to the same function-writer adding or changing their non-checked exception, for which an 1+ levels removed consumer gets no warning at all until the pager goes off because the system broke in production.
Unless the library upgrade is also fixing a 9.9 CVE.
That same scenario (an emergency version-change to a direct dependency) could also remove a function that your code calls! Yet that does not mean mean compiler-checks are bad, or that the solution is to make a system that lets you yeet it into production anyway.

Look, I get it: Sometimes a Checked Exception defined in a niche spot "infects" higher-level code which adds it to their signatures, because nobody takes the time to convert it into something more layer-appropriate.

But that is the exact same kind of problem you'd also get when library's NicheCalculationResult class is trickling upwards without conversion too! However nobody freaks out over that one. Not because it's mechanically different, but because it's familiar.

> That same scenario (an emergency version-change to a direct dependency) could also remove a function that your code calls!

absolutely, but the catch is it doesn't affect me transitively. the immediate caller must deal with the issue somehow. with exceptions, it is expected to not handle the ones you have no business handling, so you should change your signature. this propagates upwards and there is no layer of abstraction that can handle this problem without breaking the world. the only somewhat sane way is wrapping the new exception is something that you already handle - if that makes logical sense, which it very well might not.

> NicheCalculationResult class is trickling upwards

yes, and yes people do freak out, not sure why you think they aren't :)

(comment deleted)
Nah, I think having a monadic Maybe/Option type and first-class support for it is the correct solution. Exceptions are fundamentally flawed.
"None" to represent any type of failure sounds similarly fundamentally flawed too.
That's why you need not just an Either/Result type, but proper monads so that you can use the same functions with both.
Yes, but then you have to handle every possible errors at every call point, and wrap all those you can't handle at the calling site into your own return type... This is well documented.

One should be cautious every time it feels like there is an obviously right way to do something that everybody fails to see :)

> Yes, but then you have to handle every possible errors at every call point, and wrap all those you can't handle at the calling site into your own return type... This is well documented.

What? No you don't. You can just propagate up the errors you can't handle with the types they come with.

What language are you using that will automatically infer this at compilation time?(*)

If I understand properly what you are suggesting, to make it work with my goto language (OCaml), I believe that I would have to make every function return a polymorphic variant for the error (aka technicaly equivalent to mandatory try blocks and boxed return types). The only time I had to deal with a library doing that it was not pleasant but it was too long ago for me to remember the details, probably related to complex compilation error messages.

(*) looking at your github, I guess Scala?

As someone who worked with Haskell and Rust in production this is a nightmare: - All functions end up returnig `Either/Result` - Stack traces are gone - Exceptions and panics can still creep so it's not even "safer" - There is no composability of different result types, you need something like `Either<FooError | BarError | ..., A> which is not supported in most languages (I think Scala 3 and Ocaml have this feature), or you create a "general wrapper" like `SomeException` (Haskell) or use `anyhow` (Rust).

Today I write C# at my job and I could not be happier with the usage of exceptions.

Stack traces are valuable and important, agreed. (In Scala it's pretty normal to use Either/Result with an exception type on the left so that you still get the stack trace). And it's definitely possible to carry an error state around too far when it's not recoverable and you should have just errored out earlier - ultimately you still need good coding judgement. "All functions end up returnig `Either/Result`" is a sign you're doing something wrong.

> Exceptions and panics can still creep so it's not even "safer"

This part is just as true for checked exceptions - you still have unchecked exceptions too. Ultimately you can't get away from needing a way to bail out from unrecoverable states. But "secondary state that is recoverable and understandable, but should be handled off the primary codepath" is a very useful tool to have in your vocabulary.

> Today I write C# at my job and I could not be happier with the usage of exceptions.

How do you do e.g. input validation? Things that are going to be 4xx errors not 5xx errors, in HTTP terms. Do you use exceptions for those? (I note that C# doesn't have checked exceptions at all, so there's no direct equivalent)

> This part is just as true for checked exceptions - you still have unchecked exceptions too

That's why I'm against checked exceptions in general: I avoid them in Java as much as possible.

> "All functions end up returnig `Either/Result`" is a sign you're doing something wrong.

I agree, that's why I'm sharing that it's a bad idea. I've had this experience recently in a team of very experienced developers (some of them who even have books written). In the wild you have Go in which essentially all non-trivial functions return `X, error`.

> How do you do e.g. input validation? Things that are going to be 4xx errors not 5xx errors, in HTTP terms

Recently I've been working on a piece of code that deals exactly with that. For such use cases we use the `bool Try(out var X)` pattern which follows what you have in the standard library (see `int.TryParse(string input, out int result)`) which works but I'm not a fan of. For this use case a `Either/Result` would work great, but is a very localized piece of code, not something that you would expect to see everywhere. Also, you might want to roll (or use) a specialized `Validation` type that does not short-circuit when possible (ex. in a form you want to check as many fields as possible in a single pass).

In summary, I'm not against the existence of `Either/Result` in general, I think there are great use cases for them (like validation); what I'm against is the usage of them to signal all possible type of errors, in particular when IO is involved.

> In summary, I'm not against the existence of `Either/Result` in general, I think there are great use cases for them (like validation); what I'm against is the usage of them to signal all possible type of errors, in particular when IO is involved.

I agree that a checked error type is a bad way to represent IO errors that most programs won't want to handle (or will handle in a very basic "retry the whole thing at high level" way). I think a lot of functional IO runtimes are converging on a design where IO actions implicitly carry some possibility of error that you don't have to nest in a separate either/result type.

Effect systems such as Bluefin (my own) and effectful allow you to have multiple possible exception effects in scope without having to cram them all into one type (with some sort of "variant" or "open sum" type). It's a very pleasant way to work!
Aren't checked exceptions the same as effect systems in the narrow case of error handling?
I haven't had the time to play with Bluefin (I'll get to it eventually!) but I did try out effectful. In the later I've only used the `Fail` effect since most of the time I just want to bail out when some invariant is broken. In my experience these fine-grained errors don't provide much value in practice but I understand the appeal for some.
> They just needed some syntactic sugar to help redirect certain developers into less self-destructive ways of procrastinating on proper error handling.

Syntactic sugar it needs is an easy way (like ! prefix) to turn it to a runtime exception.

Procrastinating on exceptions is usually the correct thing to do in your typical business application - crash the current business transaction, log the error, return error response. Not much else to do.

Instead the applications are now littered with layers of try-catch-rethrow (optionally with redundant logging and wrapping into other useless exceptions) which add no benefit.

The try/catch/rethrow model can easily be substituted by just adding a `throws` to the method. If you truly don't care, just make your method `throws Exception` or even `throws Throwable` and let the automatic bubbling take care of making you handle exceptions at top level.
That (or rather checked exceptions in general) doesn't play well with lambdas / streams.
How many errors are actually recoverable. I bet most thrown exceptions could be replaced with a printf(“it went wrong here”) for all their utility.
Well, usually you want to handle it at some level - e.g. a common REST exception handler returning a standard 500 response with some details about what went wrong. Or retry the process (sometimes the errors may be intermittent)
I disagree. The real value of exceptions is you can skip 6 levels of functions that have lines like

status = DoThing(); if(status != allIsWell) {return status;}

C++ embedded for a long time has said don't use exceptions they are slow. However recent thinking has changed - turns out in trivial code exceptions are slow but in more real world code exceptions are faster than all those layers if checks - and better yet you won't give up on writing all the if checks. Thus embedded projects are starting turn exceptions on (often optimized exceptions with static pre allocated buffers)

The final "print something when wrong" is of little value, but the unwinding is very valuable.

Do you have any references for that? I used to avoid exceptions on small Cortex M0/M3 devices as well.
> The real value of exceptions is you can skip 6 levels of functions that have lines like

For some reason some Go programmers think those lines are the best thing since sliced bread.

I mean on paper they're a good idea and well implemented etc. However, the main two flaws with exceptions are that one, most exceptions are not exceptional situations, and two, exceptions are too expensive for dealing with issues that aren't exceptional like that.
The problem with checked exceptions is they are best explained and utilized in a single threaded execution model where the exceptions can be bubbled up to the operator.

This is not, of course, the only way that checked exceptions can be utilized. But, all too often, that is by far the easiest way to reason on them. They represent a decision that needs to be bubbled up to the operator of the overall system.

Worse, the easy way to explain how the system should resume, is to go back to where the exception happened and to restart with some change in place. Disk was full, continue, but write to a new location. That is, having the entire stack unrolled means you wind up wanting the entire process reentrant. But that is an actively hostile way to work for most workflows. Imagine if, on finding a road was closed, a cab driver took you back to pick up location to ask what you want to do about it.

If it is not something that you want to unwind the stack, or bubble up to the users, then you go through effort to wrap it so that it is another value that is being processed.

The most common pattern in languages with explicit error handling, is to simply return the error (possibly with some context added) in every function up to the point where the process was started (e.g. an HTTP endpoint handler, or the CLI's main function) to deal with it.

I'm not saying exceptions are good, but I am saying that they do represent the most common error handling pattern.

Right, this is largely the same idea. For things that have to be bubbled up, you wind up in the simplistic "single thread of execution by an operator" pattern. And, in that scenario, exceptions work exactly the same as just returning it all the way up. It is literally just making it easier to unwind the stack.

My assertion is that actual error handling in workflows doesn't work in that manner. Automated workflows have to either be able to work with the value where it was broken, or generally just mark the entire workflow as busted. In that scenario, you don't bubble up the exception, you instead bubble up an error code stating why it failed so that that can be recorded for later consideration. Along the way of bubbling up, you may take alternative actions.

Thanks for the additional clarification!
Certainly! And please let me know if I'm off or otherwise confused here. I can guarantee I'm not the brightest bulb around here! :D
I'm not a fan of checked exceptions because they force you to use a verbose control structure (try-catch) that distracts from the actual logic being expressed. Typically, checked exceptions are also not exceptional, so I prefer working with monadic exceptions, like Rust's Option/Result, because they encourage error recovery code to use normal control flow, keeping it consistent with the rest of the application.

I also find that generally exceptions can't be meaningfully caught until much higher in the call-stack. In which case, a lot of intermediary methods need to be annotated with a checked exception even though it's not something that matters to that particular method. For this reason, I've really come around on the Erlang way of doing things: throwing runtime exceptions in truly exceptional situations and designing top level code so that processes can simply fail then restart if necessary.

I've pretty much been stuck on java since 1995, I started right around 1.0. There have been some stints of Python or we have some glue code in C/C++ or whatever but we're talking 95% java.

Some of these things are mildly annoying when you think about them in theoretical terms.

But all the professional teams I've been on a (a lot) have successfully dealt with exceptions without issue. The teams settled on an exception and error handling process early in the design phase and it rarely has caused a major issue.

Yet it seems out on the internet in any place where programming languages are discussed it is an insurmountable problem that has caused all projects in Java to fail and the language died an early and unpopular death. It seems if it caused anyone huge problems it was not Java's issue but that team's issue.

There are/were other much bigger issues over the years. Memory leaks have been issues. Spring was hard for many people to deal with back around 2005 or so. The first iterations were quite bad with XML. XML in general caused a lot of issues. J2EE caused a lot of issues just because it was so badly designed early on. (Some of this was because it was birthed out of CORBA, which was itself pretty horrible.) Plenty of issues were caused by using Collections with mixed objects in them early on before Generics were introduced. Visual J++ caused havoc. Different models of web application caused a lot of havoc before we got to Javascript UIs in the browser driven by Web API back ends. JPA was a big mistake IMO. But exceptions were never really a huge problem anywhere.

So many have blamed a problem on Java when it was actually a problem with a library, component, or framework written in Java that became way more popular than it should have been. And along the way there were a lot of "developer influencer celebrities" who were listened too far far more than they should have been. Many of these guys (I can't remember one ever being a woman) sold everyone on ultra complex designs and ways of doing things and the community almost always bought in to a ridiculous degree.

I think your exception model needs to match your problem domain and your solution.

I work on an Inversion of Control system integration framework on top of a herd of business logic passing messages between systems. If I were to do all over again, then I’d have the business logic:

* return success or failure (invalid input)

* throw exception with expectation that it might work in the near future (timeout), with advice on how long to wait to retry, and how many retries before escalating

* throw exception with expectation that a person needs to check things out (authentication failure)

Unless the business logic catches it, unchecked exceptions are a failure. Discussion about what is what kind of exception is hard, but the business owners usually have strong opinions taking me off the hook.

> ORMs are the devil in all languages and all implementations. Just write the damn SQL

This week wrote an article on this, since I found there's a lot to explain wrt this topic:

https://dev.to/cies/the-case-against-orms-5bh4

On HN:

https://news.ycombinator.com/item?id=42922014

I've been loving edgedb in my typescript side projects for a number of years now. I have always hated ORMs, mostly for performance and a little bit for elegance / ergonomics. Curious if you have thoughts on a solution like that?
I like EdgeDB. The only downside I see is the lock-in, that is less of an issue with SQL.

While I have not used EdgeDB, I have used Hasura, which I really liked.

Wrt the syntax of queries, I know of https://prql-lang.org which is kind of similar of EdgeQL.

Funny that EdgeDB says on its page it tries to solve the ORM problem: I strongly believe ORMs do not solve any problem, they merely create problems (as I argue in the linked article).

Wrt my thoughts: the EdgeQL queries are usually still strings (no compile time type checking, not IDE tooling). With Hasura the GraphQL interface (that EdgeDB also supports) allows one to generate a client library based on the GraphQL schema: this allows one to have compile time checks on the queries, which is really neat.

If you like GraphQL and are looking at databases, try Dgraph community edition some day.
This is days old, but edgedb does allow generating a fully typesafe query builder package that is schema aware. At least for typescript, I haven't tried it with other languages yet.

Edgedb ends up being an extremely performant ORM if used that way. And since your database is fully aware of your object graph, it's just as or more type safe than graphql.

The author learns that Java is not a Functional Programming language; lambdas ain't enough!
> Gradual, dependently typed languages are the future

I really interested why the author thinks this.

I've seen the general sentiment wrt "the future" go from C to C++ to Java to Ruby to JS (also server-side) and Python.

My own sentiment went from Ruby to Haskell to, well, Kotlin I guess...

I looked into Idris (dependently typed), but did not think it would fit the Overton window[1], to be a reasonable expectation for the future.

1: https://en.wikipedia.org/wiki/Overton_window

Dependent typing is mainly just that feature of Typescript that `x: number | null` becomes just `x: number` inside an `if (x != null) { ... }` statement.

If you dig deeper into the type theory, it gets really interesting and complicated because types themselves can start to include any arbitrary code. And that's where Idris comes in! But after doing a whole project on Idris in college, I agree it is way outside the overturn window. Python and typescript rightfully keep it simple.

Narrowing, as TypeScript calls it, isn't dependant typing - it's basically just a form of pattern-matching over a sum type.
> to C++ to Java to Ruby to JS (also server-side) and Python

I feel that languages are going back to static typing. Newer languages such as Go, Rust, Kotlin, Swift, Dart, Nim etc are all statically typed (I can't remember a language from the last decade that is dynamically typed).

Even some dynamically typed languages are moving towards a more static typed system: for JS we have TS, and for Python we have type hints

> People who stress over code style, linting rules, or other minutia remain insane weirdos to me. Focus on more important things.

I just want to set up a linter that’s standard for the language and let CI deal with it. Anything other than that is mental to me.

Agree. People who join a project and insist on changing the default styling configs are insane weirdos. But people who dont want to use any styling solution on a project with several developers are EVEN MORE insane weirdos.
>People who stress over code style, linting rules, or other minutia remain insane weirdos to me.

Um, "minutiae", erm, uh.

> Good management is invaluable. (I went most of my career before seeing it done well)

Yes, and: it's difficult to describe, must be led from the top, and extremely difficult to evaluate from above.

If the person at the top can come down for a coffee with people who endured some bad management, and ask honest, non-loaded three questions, it can be measured qualitatively but with very high accuracy.

The three questions are:

    - What should we start doing?
    - What should we continue doing?
    - What should we stop doing?
This is an immensely powerful tool. Thanks to the awesome person who introduced me this.

Addenda: "Theory X" is something really bad. If you're working with a team which responds positively to Theory X, you have much bigger problems IMHO.

sounds just like the advice in the book "The Coaching Habit" which i thought was great
What insight does an IC have on what the person at the top even does? I can basically only comment on my interactions with direct management and I otherwise don't know how they spend their day.
That's not important - you're not discussing how to do their job, but what your own view is of your part of the work and those around you. The leader can then synthesize all these views and see if there are any surprises.

(another case similar to "you should listen to a customer when they say there's something wrong with the design of your product, but you should absolutely not pay attention to what they think the solution should be")

By having a 5 min group meeting where only the manager speaks. Manager is not allowed to talk about what direction the team should go and what the team should do. Anything else will give instant insights on whats going on at the isolated top.
Good leadership doesn't have the ideas. It leverages the collective to extract the best ideas and facilitates them.
That's true. The tool I gave above does exactly that. You distribute these three questions to your team, collect answers anonymously, then pile them up and look at what you see to read your team.

Then you can see what's going well and what's not, and plan next steps accordingly.

> Typed languages are essential on teams with mixed experience levels

I like this one because it puts this endless dilemma in a human context. Most discussions are technical (static typing ease refactoring and safety, dynamic typing is easier to learn and better for interactive programming etc.) and ignore the users, the programmers.

UX really is everywhere, once your eyes are opened to it.
Ill plug the "Design of Everyday Things" by Don Norman in case anyone in the thread hasn't read it. Its the classic text on design. You will never think about doors the same again.
I think the size of the code base also matters: bigger size = having types is more important.

There is a contradiction here as: bigger size = compile speed more important AND types slow down compilation. More advanced typing features slow down compilation even more.

> More advanced typing features slow down compilation even more.

C++ is a bit of an outlier here. But really people should think of typechecking as shifting fault detection earlier in the process than runtime. It doesn't matter if your test suite starts slightly quicker if you have to wait for the whole thing to run to find something you could otherwise have found with types.

I agree. But the nice thing about types is that they are not an afterthought, but more a "pre thought". TDD tries to make testing a pre thought...
I'm kind of wondering where the "mixed experience levels" part comes from. What is it about more homogeneously skilled teams that makes them less susceptible to the productivity boost that statically typed languages give in large code bases?
I'm reading in it that experienced developers (be it overall or in a specific codebase) "know" all the ins and outs, types, conventions etc, whereas less experienced people cannot yet know all of that; being able to lean on good types and / or other types of automated checks helps them make more confident changes.
Less experienced devs iterate on something until it looks like it works, not realizing the footguns they may have embedded. Static typing removes some footguns and provides documentation for the next unfortunate soul to look at this code.
> What is it about more homogeneously skilled teams

They are a strawman example that doesn't exist in the real world.

Companies will be in big trouble in a few years when the team retires, people find new jobs, someone dies... All of them mean that a homogeneously skilled team will exist for at most a few years if you have one. As a company you need to ensure you have a program to train in new people.

I have long believed that when someone retires you should replace them with someone fresh out of school, promoting people all the way down to fill the opening. If someone finds a new job you can replace them with someone else with similar experience, but when someone retires they should be replaced by someone you already have groomed for the job.

IMO it's like scrum: if your team is good and homogeneous, it doesn't really matter much what you do: it just works. Scrum and no scrum, types and no types. It's not about having rockstars or 10x engineers, it's just about having shorthands, shared knowledge, etc.

If your team is varied or too large, you need things to help you out with organisation and communication.

(Whether my examples of Scrum and Types are the answer: depends on the team unfortunately)

In my experience: Too large is any team larger than 10 people or any code base with more than 10,000 lines of code. Both of those would be considered tiny by most in the industry.
Oh, you are definitely correct about 10 people being too many. For me I think the magic number was 4 or 5.

About 10k lines, I really never stopped to think but I'm gonna guess you're correct on that too.

The numbers I gave are not exact. Depending on details that I don't think anyone entirely knows. Sometime 1 person is too many (generally implying a bad programmer), while other times you can get a bit over 10 if you have strong discipline. Likewise strong discipline can get you to 100k lines. Really what this is about is how much pain you are willing to put up with. 10 people and 10k lines of code are good round numbers to work with.
Hard disagree on the 10kloc limit. At a previous job, I maintained and enhanced a 50kloc monolith (written by someone else), usually by myself. My productivity was very high. At my current job, we've split a codebase that should be about 50kloc into more than 10 separate repositories; everything is still just as coupled but it's much harder to reason about and refactor. My productivity is much lower.
I would tend to agree with the author's statement there. Though less "necessary for heterogeneous teams" and more "unless your team is entirely senior/skilled".

To sum up some thoughts that have evolved over decades into something more reasonable for a comment--more junior developers are less able to develop a robus mental model of the codebase they're working in. A senior developer can often see further out into fog of war, while the more junior developer is working in a more local context. Enforcing typing brings the context closer into view and reduces the scope necessary for the developer to make sensible changes to something manageable.

It also makes it much easier to keep contracts in the codebase sane and able to be relied on. With no declared return type on some method, even with checks and code reviews it's possible there's some non-obvious branch or set of conditions where it fails to return something. Somebody might try and take a "shortcut" and return some totally different type of object in some case. In every case, it puts these things front and center and reduces the ability to "throw shit at the wall until it sort of works on the happy path" without making that much more obvious.

And once those types are declared, it saves everyone else time when stupid shit does slip through. You probably have some idea what `getProductAvailability(products)` might do in some eCommerce system. But when the actual function is implemented as `getProductAvailability(InvoiceItem[] products): void`, the foot gun for the rest of the team is... less. (Your guess as to how I know this is correct.)

In teams with good, experienced people the types are still helpful and I'd still question anyone choosing _not_ to be explicit about these sorts of things regardless of team composition. But they're much less _necessary_ in a skilled team in my experience.

Even when I am alone, I have "mixed experience levels", for example I can learn something niche, write some algorithm that works in that, then 2 years later I may have forgotten it. Types are essential for me.
The comment about project managers is interesting. At my last workplace, the PMs -- all consultants -- were the only reason anything ever got done.
Depends on team's experience level
Actually in this case it depended upon the resource managers' technical illiteracy and unwillingness to do their jobs.
> Most programming should be done long before a single line of code is written

I'd rephrase this to something like: Most programming should be done before 5% of the code is written. Because "no plan survives contact with the enemy". I often develop a plan, work for just a tiny bit, and realize some new constraints. It's after that point that you should construct your grand battle plan.

From “The Cathedral and the Bazaar”:

> # 3 “Plan to throw one away; you will, anyhow.” (Fred Brooks, “The Mythical Man-Month”, Chapter 11)

> Or, to put it another way, you often don’t really understand the problem until after the first time you implement a solution. The second time, maybe you know enough to do it right. So if you want to get it right, be ready to start over at least once.

https://en.m.wikipedia.org/wiki/The_Cathedral_and_the_Bazaar

Its wrong in those day , exploratory coding is better for dynamic languges like python using jupyter notebook , and then do proper coding after exploratory step..
I've made an opposite progression from the op. I was a strong believer of upfront design, but now value iterative approach as you do.

For the first try, hack together something working, you'll learn things along the way, validate/disprove your assumptions. Iterating on this will often bring you to a good solution. Sometimes you find out that your current approach is untenable, go back to the whiteboard and figure out something different.

It felt good to read someone who thinks like me, honestly.

Also the observation of "The trouble with functional programming is functional programmers" is absolutely correct.

This needs to be said more. Way more.

P.S.: You can ask why, and I can answer honestly, without hostility.

Maybe say it less but it explain it more. I don't get it at all.
Let me try to explain it a bit more.

From my experience, functional programmers come in two flavors. The ones who constantly nag others by telling them "everything you do is wrong", and "hey, come here, I want to show you something!". Unfortunately, the first camp is way more dominant than others.

Again, from the same article, there's observation "People who care about the craft are rare. Cherish the who cares, meet the rest at where they are". This is very true. The problem is, the people in the first camp can't cater to either group. First group has strong opinions and want a solid (not hostile, solid) discussion about how they can improve and how that tool works, and the second group doesn't care.

Also, every programmer has an understanding of the machine, and they program on top of that abstraction. For me, "C virtual machine", or the modern hardware is very easy to grasp. For every line of code, I can have an educated guess about how my code will run (e.g. how will the branch predictor will behave or got inadvertently poisoned), hence imperative languages and the primitive memory management is easy for me. For others who think more meta, functional paradigm is a natural think to construct in their brains. Not meeting them at where they are is again off-putting.

All in all, nobody wants to be constantly nagged to feel "stupid" about not understanding functional programming or how unelegant imperative is, and most of the functional programmers are doing this, even without knowing it.

Some of my friends who do functional programming are more reasonable people. Who sit down and look at some code or problem say "oh, this looks an interesting problem and interesting solution, can I show you how it's done in functional way", and we can go from there, and go a very long way from there.

I have never picked up Common LISP because of an academic who praised functional programming like it's the second coming of Christ. I still have the first chapter of the LISP book printed on my desk. I'll start it after 15 years, but now I don't have the time I want to spend on it.

Lastly, from an architecture/elegance perspective, I believe a good imperative program in C/C++/Go is somewhere between Bauhaus and Brutalist architecture depending on what domain you're targeting (lower the level, more Brutalism), and I find that naked nature of imperative code directly tapping into the hardware or the kernel efficiently very elegant and well-designed. For the same majority of functional programmers this is ugly and shall be got ridden with a flamethrower. Also, an imperative programming language is no lighter on PLT and mathematics which the same majority finds mouth wateringly elegant.

TL;DR: If the majority of functional programmers can be a bit less arrogant, meet in the middle and learn why we love what we love, we can build better languages, ecosystems, and a better world to live in, but no, functional elegant, imperative bad.

Robert Martin's Talk: "What Killed Smalltalk can Kill Ruby, Too": https://youtu.be/YX3iRjKj7C0 which notes a similar mechanic about Smalltalk.

Really? You think most functional programmers are arrogant?

Have you considered that your position is lazy, and that actually you have the problem?

How my position is lazy? By not learning Functional Programming? I openly said that I was driven away by these very people, because I don't want to be part of a community who belittles the outsiders the moment they talked.

I mean, most (not all) functional programmers I met (for the last 20 years, no less!) started to praise functional programming by bashing imperative programming languages and never asked me about what I like about programming, and why I was so adamant to stay away from functional paradigm.

When you start selling what you like as an omnipotent silver bullet without listening to what the other party is saying, or by calling the other party lazy and the root of the problem you drive away people from the thing you are selling,

like you're doing right now.

Extra points for you for doing this, even after I have politely said that I have left that beef behind and trying to find the time to learn functional programming, and PLT in depth. Chef's kiss, actually.

> How my position is lazy? By not learning Functional Programming?

No, your position is lazy by asserting that most people who do functional programming are arrogant. This is a nonsensical value judgement. You don't know most functional programmers.

I don't know whether or not you're skilled FP. I don't know you. But as is appropriate, I assumed that you do actually know what you're talking about.

No, I've seen it too. Here on HN. Not just once.

Maybe not "most functional programmers", though. Maybe "the functional programmers who post the most" or "post the most stridently" and therefore "the functional programmers that I encounter the most often in ways that let me know that they are functional programmers".

And "Have you considered that your position is lazy" isn't a reply likely to change hearts and minds. (It is also, itself, a pretty lazy reply, compared to the effort the GP put into their answer.)

My experience is the opposite, I've only met incredibly friendly and helpful people within the functional world.

Your description of a person who has to put down everyone else in other to raise himself up, is just person with such low self-esteem that it's become toxic. You'll find these people everywhere, it's not exclusive to FP.

I'm personally fairly rigid about implementing business logic as functionally as possible, but I also enjoy game development which is inherently stateful and never without some imperative parts. I do think FP has some advantages over Imperative programming in many instances, but the opposite is also true, and I'd never pretend to be smarter or better than someone like John Carmack, who's made his career almost exclusively in Imperative languages.

I think trying to fit everything into the same lens is usually the real problem. Functional programming is a great methodology for a lot of things, but there are many ways to design things. Chances are really good that something elegantly written in prolog won't be as elegant in scheme or java, and vice versa.
Yes, this is one of the bigger (if not the biggest) problems. I don't think that imperative programming is the only or the best way, either.

The different approaches can and do transform some really ugly things in one paradigm to something really neat and joyful things in another.

But reaching that level of maturity or flexibility is rather hard as far as I can see, and it's honestly sad.

> The trouble with functional programming is functional programmers

> This needs to be said more. Way more.

In other words, why stop with an ad-hominem when you can do an ad-nauseam too.

There's a comment down there which I explain why I support this claim, without any attacks, but with citations from experience.

Why not give that one a read, then come again?

Maybe we can discuss, and we can both learn something from it?

(comment deleted)
> REPLs are not useful design tools (though, they are useful exploratory tools)

I disagree with this. I’m a Clojure dev, and most of the time, I use the REPL to iterate on features, fix bugs, and refactor, thanks to the fast feedback loop.

I used to be a Java dev—oh god, restarting the whole app after every change made me want to shoot myself in the head. Now, I use the REPL to build what I want and then move on. This brings joy back to programming.

I’m not saying other languages are bad, but working with Clojure is more enjoyable for me. I’m at least 2-3 times faster than I was with Java. Of course, there are techniques you need to know to write efficient and idiomatic functional code.

I could definitely see how if you work in Java or C# all day, repls feel like a neat curiosity. Picture telling someone who writes scheme in emacs that repls aren't a good design tool.

If you want to argue the point with non lisp people, I'd go to javascript or SQL as great examples where you really use repl's quite a bit.

I'm a heavy REPL/interactive shell user so when I do Java I abuse the testing framework, basically I put my sketches in unit tests and run those. The feedback loop is pretty tight, close to what I get in some other languages, whatever happens behind the scenes in IntelliJ it's much shorter than a full recompile and boot.

Supposedly there are some Java shells around but I haven't tried them out.

Well, because Clojure actually has a "proper" REPL. Non-lispy languages don't have such REPLs, at best - they are interactive shells. The blogpost author doesn't seem to have experience with homoiconic languages, otherwise, I'm sure, that sentence would be different.
Java has been able to hot swap code since the beginning? Well, maybe not the beginning. But very early versions.

The standard runtime didn't like some redefinition. But there were alternatives. Eclipse, for example, would purposely let you get otherwise broken code running so that you could breakpoint and replace as you went.

> Most won't care about the craft. Cherish the ones that do, meet the rest where they are

> (…)

> People who stress over code style, linting rules, or other minutia remain insane weirdos to me. Focus on more important things.

What you call “stressing over minutiae” others might call “caring for the craft”. Revered artisans are precisely the ones who care for the details. “Stressing” is your value judgement, not necessarily the ground truth.

What you’re essentially saying is “cherish the people who care up to the level I personally and subjectively think is right, and dismiss everyone who cares more as insane weirdos who cannot prioritise”.

There is more than one type of people who stress over code style. There's the group who wants to discuss about how to style your code and then there's the group who wants to just use a common code formatter and be done with that.

For example, I have objections to rustfmt's default style. I would never start discussions on rust projects about changing that to another formatter or changing its configuration. I definitely would carefully ask that people should really use rustfmt, though, if they don't do so yet.

Edit: This comment was based on misreading the parent comment. I've left it up, but I should have been more careful.

You've set yourself up to always be the outlier. To always need to have that discussion or tweak the rules on every project you work with.

You've increased the overhead of onboarding anyone used to the default style. You've increased the overhead of you working on anyone else's projects that is more likely to have the default style.

All of that is friction introduced because you haven't learned to live with the default style.

Do I love that the default C# rules put a new line before the else block? No, but I've learned to live with it so that I can work on all manner of projects without fussing over that style option every time.

By adhering to default rules, you never have to have the endless arguments such as tabs vs spaces again. ( Spaces won, and the tabbers got over it fairly quickly once it was the default in almost all formatters. )

I believe you may have misunderstood my comment, as I agree.
I apologise, I misread your statement as: "I would never start discussions on rust projects *without* changing that to another formatter" which changed the meaning entirely. I should have taken a moment to re-read what you wrote.
What I understood from your parent comment was the exact opposite, i.e. that they’re saying “I disagree with some of the default choices of the formatter (and so would prefer they were different) but I never voice those because it’s not worth it. However, I do think everyone should use something, whatever it is (even if the default style), as opposed to nothing”.
> I definitely would carefully ask that people should really use rustfmt, though, if they don't do so yet.

If you don't already, you should run `cargo fmt --check` in CI and block PR merges until it passes. You can also run it in a pre-commit hook, but you can't be sure everyone will set that up.

Automating such types of decision is great but moot if others have to opt into it.

Code formatters are the best of both worlds. I despise Allman-style indentation for reasons I can't explain or justify, but if I have to work on a codebase that uses it, I can simply put hooks to reformat it to whatever the repo style is before commit.
It's not even more or less, it's just about different aspects. Consider a woodworker obsessing over sharp chisels - they don't all care beyond 'sharpish', or about the tools used to do so - but to some that's hugely important and how they are then able to do their best work.
To take it further some woodworkers are really tool hobbyists. They obsess over the tools and never really touch the wood they’re supposedly working. Same goes for software devs, I think about this when reading threads obsessing over AWS services, pipelines, build stacks, instead of writing software.
The solution is to have computers enforce the code style. Pick a linter, pick a set of rules, and then forget about them.

Things I beleive:

- If you're picking up on code-style in PRs then your toolchain is backward.

- If you're changing linting rules every month then you're focussed on the wrong things

- It's better to have a consistent style than a perfect style

This is what we did with clang-format on our code base.

The result is occasionally ugly code and nobody is 100% happy but at least it's consistent.

*cough*clang-format off*cough*
Ha, yes. I started doing that but then lost the will to continue.
Yes. I love how gofmt has no settings, basically. Is this how you envisioned? Great. Now I don't have to think about optimizing it.

Coincidentally, the choices they made are the choices I'd made, but it doesn't matter in the end.

IMO, gofmt doesn't go far enough. It should sort imports and break lines automatically, or you end up with different people with slightly different preferences for breaking up lines, leading to an inconsistent style.

Something like gofumpt + golines + goimports makes more sense to me, but I'm used to ruff in Python (previous black + isort) and rustfmt.

I'd say that if you're manually formatting stuff with line breaks and spaces, that should have been automated by tooling. And that tooling should run both as a pre-commit hook and in CI.

gofmt sorts imports within the block they are in.

I have a habit of putting stdlib imports, a line break, golang experimental (x) imports, a line break and external imports.

I just tested, gofmt orders imports within these blocks. If I won't use the line breaks, it'll consider it a single block and will globally sort.

golang also aligns variable spacing and inline comments to look them as unified blocks (or a table if you prefer the term) and handles indents. The only thing it doesn't do is breaking lines, which I think acceptable.

I meant that you could do this with isort in Python or rustfmt with `group_imports = StdExternalCrate` (sadly, not the default), where the imports are automatically separated into blocks, which is basically what you seem to be doing manually.

My point is that many things we end up doing manually can be automated, so I don't need to care about unsorted or unused imports at all and can rely on them to be fixed without my input, and I don't have to worry about a colleague forgetting to it either.

I didn't know that so many people did that so formatters have an option for this. On the other hand, I'm doing this for so long across so many languages, I never thought about it. It consumes no effort on my part.

Also, gofmt not removing the breaking lines and handling groups inside themselves tell me that gofmt is aware of these choices already and accommodates this without being fussy or needing configuration about it, and it's an elegant approach.

Be careful what you wish for. Sometimes leaving a line stupidly long is better for readability than awkwardly trying to break it up.

If you have five similar statements somewhere with only one exceeding the linter's line length, breaking up that one can lead to code that is harder to parse than just leaving it jutting out by way of an exception to the general rule; especially if it contains some predictable bit of code.

Those would be edge cases where formatting can be turned off if needed. This would require justification during code review. Otherwise, we'd keep the automatic format, with a strong tendency towards the latter.

The general benefit of automated formatting outweighs these edge cases.

Vehemently disagree. I get that go's conventions line up with your own, but when they don't, it's irritating. For example, Dart is finally coming around on the "tall" style (https://github.com/dart-lang/dart_style/issues/1253). But why should people be forced to wait for a committee (or some other lofty institution) to change a style convention that then effects everyone using the language? What's the harm in letting people write their code in the "tall" style beforehand? Why must the formatter be so insistent about it?
> Why must the formatter be so insistent about it?

Exactly to avoid conversations like this...If you want a tall format then just add a dummy comment at the end?

Except that a prescriptive formatter with zero configurability is causing this conversation.
When you are a solo dev, everything is acceptable. When you're in a group, this can cause friction and cause real problems if people are "so insistent" about their style.

Go is a programming language developed for teams. From its syntax to core library to language server to tooling, Go is made for teams, and to minimize thinking. It's opposite of Rust. It's devilishly simple, so you can't make mistakes most of the time.

Even the logo's expression is an homage to this.

So yes, Go's formatter should be this prescriptive. The other thing is, you can continue this conversation till the end of time, but you can't argue with the formatter. It'll always do its thing.

In other words, "computer says no".

You are misunderstanding the argument: I am not against prescriptive formatters. By all means, enforce a style convention for your project, I have nothing against that, nor do I understand how you interpreted that from my comment. I am against prescriptive formatters that cannot be configured. This creates the absurd situation, as previously described, where one must appeal to 'The Committee' who decides the style convention for the entire world. This should not be necessary. Nor should you have to surround your code with formatter-disabling comments (assuming the language even supports that) to, for example, use the "tall" style, as previously mentioned. Nor should you have to literally fork the language or its tools to disable the formatter.
If something can be configured, this opens up infinite possibilities for discussions on how it should be configured. The fact that you even ASK if you should use the tall style means that you are considering the POSSIBILITY of using it that way.

Put it this way, if it is technically impossible to develop a car that have the color red, we would not be discussing or entertaining what color the next car should have. It would be red, end of story. It doesn't matter if we like red or not, it just has to be. end of story.

Yes, the code looks awful. end of story.

Hot take: stop being authoritarian with code styles, perchance? You are not so wise as to determine what is clean code for the entire world, and the ego required to believe that you are is beyond astounding. And we're still having these discussions despite unconfigurably prescriptive formatters. This idea that such formatters end these discussions is just demonstrably false... you are literally taking part in one of these discussions.
The point is, that the way it's formatted doesn't matter. It just matters that it's consistent. Consistency is better than being right.
As I've said, it's fine for people, projects, and organisations to require and enforce a particular style for their code, to demand consistency for their code. The problem comes when people with inflated egos believe they have the right to dictate how the entire world writes their code. I feel like I need to stress that this is the issue here.

The fact that Dart's formatter FAQ tells developers who are unhappy with the output to change their code to satisfy the formatter (https://github.com/dart-lang/dart_style/wiki/FAQ#i-dont-like...), rather than allowing developers to tweak the formatter to satisfy their code, is such a huge tell about their mindset. And again, these global styles do not "end the discussion" as people in this thread have asserted. The "tall style" PR (https://github.com/dart-lang/dart_style/issues/1253) refutes that. And that PR has since devolved into bickering and heated demands since its adoption into SDK 3.7.0, since it transforms people's code in ways they dislike and/or find less readable.

Just let people, projects, and organisations enforce their own styles conventions. It's not hard and it shouldn't be controversial.

While Python has some great linters, I don't know of any in C that can correctly and automatically enforce some coding style. Most of them can only indent correctly, but they can't break up long lines over multiple lines, format array literals, or strings. Few or none knows how to deal with names or preprocessor macros.
clang-format and clang-tidy are both excellent for C and C++ (and protobuf, if your group uses it). Since they are based on the clang front-end, they naturally have full support for both languages and all of their complexity.
Agreed.

A healthy toolchain lets a developer separate the Craft from the minutiae.

100%. Enforcing lint rules is very important. What those lint rules should say is generally very unimportant because the editor should be doing all the work, and most of the time "that's just like, your opinion, man".
Agreed. I don't care what the indentation is or formatting is, as long as it's consistent.
I sometimes care when I want to introduce empty space to visually make parts of code stand out, eg multiple blank lines between functions or classes etc. I think whitespace can be effectively used like paragraphs in a book, basically make different blocks more obvious. Most formatters just squash everything to be one empty line apart, for me it can be annoying.
in my experience half the people don't bother installing the tools. They just don't care about formatting/linting, at all.
Just don’t make it optional.
Completely agree on all points. I used to have a style that I preferred and really wanted to use everywhere but nowadays I just throw prettier at it and take the defaults for the most part. I’ll take consistent code over mixed styles every day.
I know this sounds insane, but I used to work on some big svn codebase with many developers, without any fancy formating tools AND no one cared about consistent style.

One interesting thing that happened was the ability to guess who wrote what code purely based on coding style.

This happens to some degree in linted code bases since there’s more than one way to write the same code, and people reach for what they like
Not really. Obsessing over breaking lines after famous 80 chars in Eclipse was, is and will be idiotic to be polite. Surprisingly large amount of people were obsessed by this long after we got much bigger screens, if that was ever an argument (it wasn't for me). 2 spaces vs 4 spaces or tab. Cases like these were not that rare, even though now it seems better. That's not productive focus of one's (or team's) energy and a proper waste of money for employer/customer, it brings 0 added value to products apart form polishing ego of specific individual.

Folks who care about the craft obsess (well within realm of being realistic) more about architecture, good use of design patterns, using good modern toolset (but not bleeding edge), not building monolithic spaghetti monster that can't evolve much further, avoiding quick hacks that end up being hard to remove and work with over time and so on.

If you don't see a difference between those groups, I don't think you understood author's points.

I'd say avoiding long lines is one of the most important rules. I regularly have 2-3 files open side by side, I don't want to have to scroll sideways to read the code.

80 characters is a bit on the low end imo but I'd rather have the code be too vertical than too horizontal. Maybe 120-150 is a more reasonable limit. It's not difficult to stay within those bounds as long as you don't do deep nesting which I don't really want to see anyway because it's hardly ever necessary and it makes code more difficult to read.

Functional code is more chained and need more space often. Descriptive names are better; tends to be longer. Buy ultrawide.

80 is for aholes who like to use small laptop and then force it on everyone else. 120/150 is reasonable.

200 is great.

200 is entirely too fucking long, and I code on a 43” 4K. I try to stay under 90 in deference to others, and if it looks better breaking at 80, so be it.
200 allowed doesn't mean most lines will be. In general, 99% will be around 150 with a few places going high if it makes sense.
Yep.

And the laptop excuse is not even valid, I used a 11" MacBook Air for 10 years and even back then 80 always felt extremely limiting for me.

I just tested and: even when zooming +1 on VSCode and leaving the minimap open I can fit 140 chars without any horizontal scroll.

People demanding 80 columns always have some crazy setups, like an IDE where the editor is just a minuscule square in the centre, like an Osbourne 1 computer.

And 140 chars aren't enough for two files side by side with 80 chars. With a readable font size and a narrow font about 90 chars is a good limit on a 14" laptop screen. Coincidentally that same limit then allows for three files side by side on the average desktop screen - or a browser window at the side for reference.

If you can live with a single file on screen that's great, but the utility of two is far greater than having a chunk of the screen empty most of the time because of a few long lines.

If you need two files on the screen side by side, then it's time to use something bigger than the MacBook 11 with zoomed fonts from my example. :)

...unless you really want to prove grandparent's point.

If you do important work frequently on 14" screen and 2 files side-by-side (regardless of its resolution), then you are seriously self-limiting yourself and your efficiency, plus hurting your eyes which will inevitably bring regrets later. That's not how 'love for the craft' or ie efficiency looks like.

One reason I like longer lines, in those very few cases (way less than 1% of code lines) - it bundles logically several easy-to-read things, ie more complex 'if' or larger constructors. We talk about Java here just to be clear, for more compact languages those numbers can get lower significantly but same principles apply.

Doing overly smart complex one-liners just for the sake of it goes completely against what we write here, I've seen only (otherwise smart) juniors do those. Harder to debug, harder to read, simply a junior show-off move.

Try saying that again when you are 50 and your eyes no longer as good as they used to be. Back when I was 25 I loved the tiny fonts I could fit on my (then incredibly large) 19 inch monitor which I had pushed to the highest resolution. These days even with special computer glasses (magnification and optimized for computer distance) I can't make such tiny text.

Now get off my lawn you punks!

Crazy.

I am.

And that's why I said I use the zoom in VSCode in my previous message. My font is big as fuck compared to everyone else.

Also old. Also can not read the fonts my earlier self would use. Still do not understand the love for narrow columns.
I understand the age issue but you can always get a 42" display? LG C2/3/4 are amazing.
Reading vertically is much faster than reading horizontally, so I think 80 is a good soft limit. In some contexts it's hard to not go over it sometimes, e.g. in Java where it's not uncommon with very long type and method names.
Reporting as an Eclipse user of 20+ years, and a person who cares about the craft:

The choice for me is simple:

If I'm going to view the code I'm writing in a 80x24 terminal later on, I'll break that lines, and will try really hard to not get closer to 80 chars per line.

If that code is only going to be seen in Eclipse and only by me, I won't break that lines.

I omitted your other examples for brevity.

Having bigger screens doesn't make longer lines legit or valid. I may have anything between 4-9 terminals open on my 28" 2K screen anytime, and no, I don't want to see lines going from one side to another like spikes, even if I have written them.

I like 80 columns, I can tolerate 100 or 120. I get really annoyed with formatting standards, JS/TS in particular that waste a whole line for a closing brace. Standard aspect has screens more limited vertically than horizontally.

When dealing with tabular data, particularly test data, I find most formatting lacking. I want to be able to specify blocks that align on the decimal point. Especially when dealing with lists of dicts. This makes reading test fixtures much more intuitive than default indentation styles.

Has anyone seen a formatter where you can specify a block be formatted in that manner?

  [{'a':  3.89, 'b':  10},
   {'a': 12.3,  'b': 233}]
instead of

  [{'a': 3.89,
    'b': 10},
   {'a': 12.3,
    'b': 233}]
There's a correlation-is-not-causation issue here. Yes, most very good developers pay a lot of attention to details, as well as to the bigger picture. But there are a lot of mediocre to downright bad developers who think that paying a lot of attention to code style, linting rules and other minutia will make them better developers.
My experience has been the opposite. Most mediocre to bad developers are so happy that their code works AT ALL that they pay little to no attention to things like sensible variable names and whitespace conventions. We sometimes call these things, "code smell."
I've always thought on the contrary that "code smell" refers much more to the patterns used and not so much how it was formatted. Anyway, choosing a language that has automatic formatting (Go for example) renders about 80% of comments in this thread about "linting" and "formatting" and blah blah blah completely irrelevant.
as others note, most of this minutia can be automatically enforced. Anything that can be automated is not craft.
Heartily disagree. I have had to fight with others over automated formatting because it obscured intent or clarity. For example (C#) mandating `var`, mandating one and only one line of whitespace between lines of code, mandating the => syntax for single-line functions (instead of

Function(vars) { ... } )

I would say, when to use one or the other of each of those options is very much a craft.

Sadly formatting well written pandas and Polars code cannot be automated but current tooling.
In my opinion, I think the author is criticizing bike shedding [1] rather than meaningful decisions. Of course some people will differ on whether a decision is one or the other. But as a whole, not sweating the details is a good quality to have whatever road in life you are on.

[1] https://en.wikipedia.org/wiki/Law_of_triviality

Everyone dislikes bikeshedding. But people disagree on which questions are bikeshedding and which aren't.
Details are important though. Some bikeshed type ideas spiral into very expensive changes. This is a large part of why US transit construction is so much more expensive - people asking for lots of little details which add up (large monument stations, bike paths done with the project... those things all add up) - the important safety details are left to experts, but only after they are told to build something far more expensive than needed. (this isn't a plea for brutalism architecture there are nice things you can do that are only minimally more expensive)
There's another way to look at this: if you consider the school of thought that says that the code is the design, and compilation is the construction process, then stressing over code style is equivalent to stressing over the formatting and conventions of the blueprint (to use a civil engineering metaphor), instead of stressing over load bearing, material costs and utility of the space.

I'm fond of saying that anything that doesn't survive the compilation process is not design but code organization. Design would be: which data structures to use (list, map, array etc.), which data to keep in memory, which data to load/save and when, which algorithms to use, how to handle concurrency etc. Keeping the code organized is useful and is a part of basic hygiene, but it's far from the defining characteristic of the craft.

> the school of thought that says that the code is the design

There's such a school?? Seriously??

architecture over implementation (for details).
> the formatting and conventions of the blueprint

Some of those formatting conventions are written in blood. The clarity of a blueprint is a big deal when people are using it to convey safety critical information.

I don’t think code formatting rises anywhere close to that level, but it’s also trying to reduce cognitive load which is a big deal in software development. Nobody wants to look at multiple lines concatenated together, how far beyond that you take things runs into diminishing returns. However at a minimum formatting changes shouldn’t regularly complicate doing a diff.

I 100% agree. The problem is that after a half a century, software engineering discipline has been unable to agree on global conventions and standards. I recently had an experience where a repair crew was worried about the odd looking placement of a concrete beam in my house. I brought over the blueprints, and the technician found the schedule of beams and columns within seconds, pinpointed the beam and said, "Ah, that's why. We just need to <solution I won't go into>". Just then it struck me how we can't do this in software engineering, even when the project is basically a bog-standard business app: CRUD API backed by an RDBMS.
That’s because the few hard rules you have to comply with have workarounds and matters rarely. In house construction, you have to care about weight, material degradation, the code, etc… there’s no such limitation on software so you can get something to work even if it’s born out of a LSD trip.

But we do have some common concepts. But they’re theoretical, so only the people that read the books knows the jargon.

I mean why should we expect software to have hard rules? Flexibility is the point, no?
No. Flexibility is a side effect. Sometimes it's a useful side effect, other times it bites you in the ass.
The rules are what make it flexible. The rules let me understand what the heck is going on in the code you wrote so I can change it. Code that is faster to rewrite from scratch isn’t flexible.
> I brought over the blueprints, and the technician found the schedule of beams and columns within seconds

Is that really an example of the standardization you want? It shows that the blueprint was done in a way that the technician expected it to be, but I am not sure that these blueprints are standardized in that way globally. Each country has its standards and language.

If an architect from a different country did that blueprint, I would bet that it would be significantly different from the blueprint you have.

Software Engineering doesn't have a problem with country borders, but different languages would require different standards and conventions. Unless you can convince everyone to use the same language (which would be a bad idea; CRUD apps and rocket systems have different trade-offs), I doubt there could be an industry-wide standard.

But I can't look at the design from my desk-mate and hope to understand it quickly. We wall love to invent as much as possible ourselves, and we lack a common design language for the spaces we are problem solving in. Personally I don't entirely think it's a problem of discipline of software engineering, but a reflection of the fact that the space of possible solutions is so high for software, and the [opportunity] cost of missing a great solution because it is too different from previous solutions is so high (difference between 120 seconds application start and 120 milliseconds application start, for instance).
Construction and civil engineering have been unable to agree on global conventions and standards, and they have a multi-millenia head start over software engineering. The US may claim to follow the "International Building Code", but it's just called that because a couple of small countries on the Americas have adopted it. For all intents and purposes it's a national standard. Globally we can't even agree on a system of units and measurements, never mind anything more consequential than that.
I’d say that globally we have agreed on a system of units and measurements. It’s just the US and a handful of third world countries that don’t follow that system.
Only USA, Myanmar and Libera are switching to metric
You don’t think of those other two having their shit together. — Archer international man of mystery.
That is largely due to a difference in complexity.I would say that the level of complexity of a blueprint of a house is on par with a 20-30 line python solution of a leet code easy excercise to a programmer. If the one reading the blue print is an engineer and the one reading the python code is a programmer. A crud app is more like the blue prints for a vacuum cleaner or something like that.
I don't know that we'll ever be able to agree on "global standards" though. Software is too specialized for that.

The only software standard that I'm reasonably familiar with is https://en.wikipedia.org/wiki/IEC_62304 which is specific to Medical Devices. In a 62304-compliant project, we might be able to do something like your example, but it could take a while. OTOH, I'm told that my Aviation counterparts following DO-178 would almost certainly be able to do something comparable.

It is going to be very industry dependent, where "industry" means aviation, or maritime, or railroad, or Healthcare, etc... Just "software" is too general to be meaningful these days.

The very point of ISO is to define international standards, many of which are used in software engineering.
Yes, of course. My point is that those standards apply only to specific domains where software is applied, not to the software development industry as a whole.
> The problem is that after a half a century, software engineering discipline has been unable to agree on global conventions and standards.

It can't, and it won't, as long as we insist on always working directly on the "single source of truth", and representing it as plaintext code. It's just not sufficient to comprehensibly represent all concerns its consumers have at the same time. We're stuck in endless fights about what is cleaner or more readable or more maintainable way of abstracting / passing errors / sizing functions / naming variables... and so on, because the industry still misses the actual answer: it depends on what you're doing at the moment. There is no one best representation, one best function size. In fact, one form may be ideal for you now, and the opposite of it may be ideal for you five minutes later, as you switch from e.g. understanding the module to debugging a specific issue in it.

We've saturated the expressive capability of our programming paradigm. We're sliding back and forth along the Pareto frontier, like a drunkard leaning against a wall, trying to find their way back to a pub without falling over. No, inventing even more mathematically complex category of magic monads won't help, that's just repackaging complexity to reach a different point on the Pareto frontier.

Hint: in construction, there is never one blueprint everyone works with. Try to fit all information about geometry, structural properties, material properties, interior arrangement, plumbing, electricity, insulation, HVAC, geological conditions, hydrological conditions, and tax conditions onto a single flat image, and... you'll get a good approximation of what source code looks like in programming as it is today.

(comment deleted)
I think we have to think of software like books and writing. It is about conveying information, and while there are grammatical rules to language and conventions around good and bad writing, we're generally happy to leave it there because too many rules is so constructive as to remove the ability to express information in the way we feel we need to. We just have to accept that some are better writers than others, or we like the style of some authors better.
That would make sense if code was written solely for the enjoyment of its readers, but it isn't.

Code uses text, sometimes even natural-language prose, but isn't like "books and writing". It has to communicate knowledge, not feels. It also ultimately have to be precise enough to give unambiguous instructions to a machine.

In this sense, code is like mathematical proofs and like architectural blueprints, which is a different category to drawings, paintings and literature. One is about shared, objective, precise understanding of a problem. The other is about sharing subjective, emotional experiences; having the audience understand the work, much less everyone understand it the same way, is not required, usually not possible, and often undesirable.

It's telling I think that the discussion becomes about standards, which software people like, rather than say, clear communication to technical stakeholders beyond the original programmer. I have a list somewhere I made of everyone that might eventually need to read an electronics schematic. Particularly to emphasize clarity to younger engineers and (ex-)hobbyists that think schematics are a kind of awkward source code or data entry for layout. It's not short: test, legal, quality control, manufacturing, firmware, sustaining/maintenance, sometimes even purchasing, etc.

Whoever drew your blueprints knew they would be needed beyond getting the house up. What would the equivalent perspective and effort for software engineering be?

Fortunately nowadays formatting issues can be delegated to autoformatting in any popular language.

Some people still argue over autoformatter parameters, but then people will always find a bike shed to argue about.

Maybe a new paradigm for code formatting could be local-only. Your editor automatically formats files the way you like to see them, and then de-formats back to match the codebase when pushing, making your changes match the codebase style.
It's a decent idea, but it's weird reviewing code you wrote in saying GitHub, it looks totally different. Imo not a show stopper but a side effect you have to get used to.
I’d like to think that GitHub would be able to display with my editor format settings.
This is pretty common now. At least my Vim/git combo does this, where I always open source code with my preferred formatting but by the time it's pushed to the server it's changed to match the repo preferences.
this works for pure formatting, but falls apart when you start linting to exclude certain syntax or patterns
This is disastrously easy to implement with just a few filters on git (clean & smudge).

I highly recommend it though, especially if you worked for a long time at one company and are used to a specific way of writing code. Or if you like tabs instead of spaces...

This was such a relieve for us. Looking back its unbelievable how much combined time we wasted complaining about and fixing formatting issues in code reviews and reformatting in general.

With clang-format & co. on Save plus possibly a git hook this all went away. It might not always be perfect (which is subjective anyway) but its so worth it.

First thing I thought of was the tiny Stone Henge in Spinal Tap.
> However at a minimum formatting changes shouldn’t regularly complicate doing a diff.

If the code needs to be reformatted, this should be done in a separate commit. Fortunately, there are now structural/semantic diff tools available for some languages that can help if someone hasn't properly split their formatting and logic changes.

Reformatting comitts still leaves the issue that you deprive yourself of any possibility to reliably diff across such commits (what changes from there to there?) Or attribute a line of code to a specific change (why did we introduce this code?).

What we should have instead is syntax-aware diffs that can ignore meaningless changes like curly braces moving into another line or lines getting wrapped for reasons.

> What we should have instead is syntax-aware diffs that can ignore meaningless changes like curly braces moving into another line or lines getting wrapped for reasons.

These diffs already exist (at least for some languages) but aren't yet integrated into the standard tools. For example, if you want a command line tool, you can use https://github.com/Wilfred/difftastic or if you are interested in a VS Code extension / GitHub App instead, you can give https://semanticdiff.com a try.

Software, being arbitrary logic, just doesn't have the same physical properties and constraints that have allowed civil engineering and construction code to converge on standards. It's often not clear what the load bearing properties of software systems are since the applications and consequences of errors are so varied. How often does a doghouse you built in your backyard turn into a skyscraper serving the needs of millions over a few short years?
This is the best argument I've ever heard for editorconfig, commiting a standardised format to the repos, and viewing it in whatever way you want to view it on your own machine
I don't care about code styles of my colleagues, as long as it remains consistent and sane. But sadly it usually isn't.
There is definitely something to be said for the idea that a shitslop engineering blueprint which still conveys correct design (maybe; who knows really?) is shitslop, whether or not the design is sound. In the case of software engineering, the blueprint is the implementation, too, so it’s not just shitslop blueprinting, it’s also shitslop brickwork (totally sound bones though I promise!), shitslop drywalling, shitslop concrete finishing and rebar work — and maybe it’s all good under the hood! Totally fine if all you’re building is a shithouse! But I think you get where I’m going with this.
The value of software is both what it does now (behavior), and what you can get it to do later (structure). What you described as design and the compiled artiface is the behavior.

The craft is what gives you future choices. So when people cares about readability, writing tests, architecture, they’re just making easy for them to adjust the current behavior later when requirements change. A software is not an house, it doesn’t get build and stays a certain way.

experience gives you some possible ideas for how it will be used in the future, but after a long time I'm coming to the position you're fooling yourself if you think you can predict where with any accuracy. It's still valuable and important to try, just not as critical to be right as I used to think. Example: I've completely flip-flopped from interface simplicity to implementation simplicity.

I agree that a house is a bad analogy, for your reasons and because you can "live" in software that as a building would not be fit for human habitation.

You have to be pragmatic about it, balancing between the speed of only implementing the now and the flexibility of taking care of the future. It's not predicting, mostly it's about recognizing the consequences of each choice (for later modifications) and and either accepting it or ensuring that it will not happen.
> It's not predicting, mostly it's about recognizing the consequences of each choice (for later modifications)

This is the exact trap I'm describing. It sounds very reasonable, but how is it not prediction when you're asking people to "recognize the <future> consequences of each choice"? You have very little to no understanding of the context, environment or application of today's creations. Smart, experienced people got us into the current microservice, frontend JS, "serverless" cloud messes.

Risk management is a fact of all activities. It's not predicting that some thing is going to happen, but it's evaluating that if we can afford the consequences if it really happens. If we can, let's go ahead with the easy choice. If we cannot, let's make sure that it won't affect us as much.

> Smart, experienced people got us into the current microservice, frontend JS, "serverless" cloud messes.

Those are solutions to real problems. The real issue is the cargo cult, aka "Google is doing it, let's do it too". If you don't have the problem, don't adopt the solution (which always bring its own issues). It's always a balancing act as there is no silver bullet.

> not the defining characteristic

Did anyone claim otherwise? Besides, I imagine bridges aren't rebuilt every week -- poor blueprints can only cause a finite amount of pain.

Let's talk about in the way you seem to interpret it.

Imagine if Blueprint A used imperial units while others used metric units.

That's what inconsistent code style does to you.

No it doesn't, the units remain the same. Only legibility changes. Style doesn't affect semantics.
>I'm fond of saying that anything that doesn't survive the compilation process is not design but code organization.

Maybe not at the same level, but code organization is also a design.

I don’t care that much about the exact linter rules applies (but I do prefer blue, hmm, nooo). But getting rid of merge conflicts that come from lake of common linter rules, this is a great pipeline process improvement, and this is some kind of code contribution pipeline design.

In many languages, types don't survive the compilation process (e.g. TypeScript, Java). Yet types describe which data structures are used.
> stressing over the formatting and conventions of the blueprint (to use a civil engineering metaphor)

This is incredibly important.

This is the kind of stuff that prevents shit like half the team using metric and the other half thinking they're imperial, or you coming up with the perfect design, but then the manufacturer makes a mirrored version of it because you didn't have the conventions agreed upon.

Imperial vs Metric is a hard requirement not a convention or formatting. I have a co-worker who wants everything to be a one liner, doesn't like if/else statements, thinks exception handling is bad and will fail a code review over a variable that he feels isn't cased properly.

This makes code reviews super slow and painful, it also means you aren't focusing on the important stuff. What the code actually does and if it meets the requirements in functionality. You don't have time for that stuff, you are too busy trying to make this one person happy by turning an if else into a ternary and the end of sprint is a day away.

> will fail a code review over a variable that he feels isn't cased properly

Many projects have fairly strict rules about identifier naming conventions, so this doesn't feel so far fetched to me.

The example about if/else vs ? : is pretty damning though.

Sure, but if there are actual rules, agreed to and accepted by the entire team (as opposed to one guy's idiosyncratic preferences) then there should be a commit-hook to run the code through a linter and reject the commit if the rules are violated.
Yes indeed. I'm a fan of getting the team to pick an already existing lint ruleset and then doing this. You can also set to only lint changed files if you want a gradual change over in existing codebase.
If a reviewer is regularly rejecting PRs because the variable names have incorrect capitalization then that's a problem with the author, not the reviewer. That is the incredibly basic shit you decide on at the start of a codebase and then follow regardless of your personal thoughts on what scheme is preferable.

If/else vs ternaries is something where consistency is a lot less important, but if you know that a team member has a strong preference for one over the other and you think it's unimportant then you should just write it how they prefer to begin with. Fight over things you think are important, not things that you think don't matter.

wtf... no.

I worked with a guy where you would try to predict what he would bitch about next. In this example, you would write it as a ternary so you don't have to hear about it ... and he'd suggest it be an if-else statement.

Nobody fucking cares which one it is; is it readable? That's the real question. Your preference of which version of "readable" it is only applies when you are the author. If you're that picky about it, write it yourself. That's what we eventually did to that guy after the team got sick of it. Anytime he'd complain about something like that, we would invite him to write a PR to our PR, otherwise, get over it. Then, we would merge our PR before he could finish.

He eventually got fired for no longer able to keep up with his work due to constantly opening refactor PR's to the dev branch.

And some specific people with lots of experience in teaching as well as in developing real life useful systems will tell you, that code is first and foremost written for people to understand, and only incidentally for a computer to run. Human understanding of what is going on is the one most important thing. If we do not have that, everything else will go to shit.
Yea, I've always considered craftsmanship to be about paying attention to the details and making everything high quality--even the things that the end user will never see, but you know are there. The Steve Jobs quote sums it up nicely:

> "When you’re a carpenter making a beautiful chest of drawers, you’re not going to use a piece of plywood on the back, even though it faces the wall and nobody will ever see it. You’ll know it’s there, so you’re going to use a beautiful piece of wood on the back. For you to sleep well at night, the aesthetic, the quality, has to be carried all the way through."

To take it a bit further, the "unseen quality" does surface periodically, like when the proverbial chest of drawers ends its first life, and goes to the dump or gets restored.

The same is true in software when a developer inherits a codebase.

Those signs of quality turn into longevity, even when they face away from the user.

If you look at back pieces of old classic furniture made during hand powered tools era, its mostly very roughly finished. Professionals rarely had time to spend dicking around with stuff that isn't visible.
The construction metaphor isn't a very good fit here in my opinion. No building is expected to have the amount of adaptability that is expected of software. It falls completely apart for interpreted languages. When is a PHP app constructed in this metaphor? On every single request? The metaphor assumes a "design once, build once" approach, which is basically no software I've ever seen used in real life. Hardware, OS, language, collaborator/dependency updates all require changes to the application code more often than not. And that's assuming the feature set stays stable, which, in my experience is also quiet rare. Maintainability is therefore a quality dimension of software, and reduced cognitive load usually results in increased maintainability (curious if someone has a counterexample to that)

That is not to say I'm one of those people who need a specific code style to have their weird brain satisfied. But not using a linter/autoformatter at all [when available] in 2025 sounds like the opposite of "work smart, not hard"

sowhatyouresayingisthatyoureequallyhappyeditingjavascriptinitsminifiedartifactformastheoriginalcode?imeanifallthatyoucareaboutisthatyourwordsarecorrectlyinterpretedbythemachinetheresnovalueinmakingsurethatyourcodeisreadableandusablebyanotherhumanbeingamiright?
So writing brittle code that’s impossible to change isn’t a failure of design? It can still compile and run just fine
I overall agree. The one thing I will say is that what you call code organization (anything pre-compilation) also includes structuring the code to improve maintainability, extensibility, and testability. I would therefore disagree that code organization is only basic hygiene, not part of design, and not a large part of the “craft” (use of that word is something I’ve changed my opinion on—while it feels good to think of it that way, it leads to exactly the thing we’re discussing; putting too much emphasis on unimportant things).

Code style though, I do agree isn’t worth stressing about. I do think you may as well decide on a linter/style, just so it’s decided and you can give it minimal energy moving forward.

By your own analogy, blueprints have a very strict set of guidelines on stuff like line styling, font selection, displaying measurements, etc. This is absolutely critical as nobody wants to live with the kind of outcomes generated when there's any meaningful ambiguity in the basic conventions of critical design documents. At the same time, having to hack through a thicket of clashing code style adds to the cognitive load of dealing with complex codebases without offering any improvement in functionality to balance the tradeoff. I've literally seen open source developers lose their commit access to projects over style issues because the maintainers correctly concluded that the benefits of maintaining an authoritarian grip on the style of code committed to the project outweighed humoring the minority of fussy individualists who couldn't set aside their preferences long enough to satisfy the needs of the community.
You defined anything that doesn't survive the balloon as keeping the s77 rad-ishes.
Thanks for writing this. It makes me question if I'm too much concerned with code hygiene vs my coworkers.
Code is read much more frequently than a blueprint.

I'd argue that code is more akin to a paper map than blueprint.

I think the line between minutiae and craft is drawn by the effect it has on the product you're creating. Method length makes your code more manageable, for example. Placement of line breaks, not so much.

At the end of the day, we aren't paid to produce code, but working software that works today and is easy to change tomorrow.

That is a purely commercial take of the matter. I don’t think it’s controversial to argue the artisans who stand out do so because they care for the craft itself. Spending an extra hour or two perfecting the shape of the armrest in the chair may not allow you to earn more money from that one commission, but it might improve your knowledge and skill and be slightly more comfortable to the sitter. If they comment on it and appreciate it, so grows your motivation and pride.

Sometimes the code itself, and not its result, is the product. For example, when making tutorials the clarity and beauty of the code matters more than what it does.

I’m not arguing for obsessing over code formatting, but pointing out the line between “master of the craft with extensive attention to detail” and “insane weirdo with prioritisation deficits focusing on minutiae” is subjective to what each person considers important. Most of us seem to agree that being consistent in a code base is more important than any specific rule, but that being consistent does matter.

At the end of the day, we aren’t paid to eat healthily and taking care of our bodies either. But doing so pays dividends. Same for caring about the quality of your code. Getting in the habit of doing it right primes you to do it like that from the start in every new project. Like most skills, writing quality code becomes easier the more you do it.

> be slightly more comfortable to the sitter

That's kind of my point: the end cannot see, or feel, minutiae. If they can, it's not minutiae.

> Sometimes the code itself, and not its result, is the product. For example, when making tutorials the clarity and beauty of the code matters more than what it does.

The code still isn't the product in that instance. It's the educational process. In many cases, clarity != beauty. This is why the best written tests often duplicate code, rather than being curated exercises in DRY.

> we aren’t paid to eat healthily and taking care of our bodies either

Yet programmers insist to be paid. Obviously taking time to grow on your own, on your own dime, is self-enriching for all the reasons you describe.

> Placement of line breaks, not so much.

this is my biggest beef with linters. i break lines following typography rules and poetry, using the line break to help communicate. hate it when the linter takes away my thoughtfully chosen line break, because i broke it there to improve readability. i seem to be the only person in the world who cares about the line break of code. other style things idgaf but you can use typography and poetry rules to improve the readability of your code.

Is it ironic that this comment has evoked a discussion on minutiae?
(comment deleted)
And that's why I don't care anymore

Yup. Doesn't matter.

Code style should be consistent and look nice, probably use the default without too much nitpicking. You have bigger fish to fry

There are better things in life to worry about

"Craft", most code goes to irrelevancy in 5 yrs. And if it doesn't it's fixable (amen for AIs)

Try to sweat up every detail right off the bat and you go nowhere

To me "craft" is about keeping code efficient, scalable, extensible, well-tested and documented. Code style is more about what naming convention to use, tabs vs spaces etc. - it's nice to have it consistent, but no need to spend more than 5 minutes arguing about it.
Tabs/spaces is a non-issue, agreed - IDEs handle it.

But surely naming convention contributes to keeping code documented, extensible and efficient?

A deviation from the norm leads to people thinking x does not exist in a large code base, leading to them implementing duplicate methods/functionality, leading to one instance evolving differently enough to cause subtle bugs but not enough to be distinct, or leading to one instance getting fixes the other does not etc?

Sample size of 1, but I've seen it happen unfortunately.

Following non functional stylistic rules to the letter as if they were on the level of SQL injections or memory leaks isn't "craft". It's cargo culture weirdness.

Sure, have a style and a linter. Be DRY. Don't lose your head over it though.

Video game logic: Everyone worse than me is a noob. Everyone better than me has no life.
Today Steam gives us the option of verifying whether a certain player has no life. Only half joking.
My favorite steam reviews are: "This game sucks, would not recommend." - playtime 800h
and on the flipside of this

"This game sucks, would not recommend." - playtime 1.2h

This is more like noob logic. Good players are eager to learn from those better than them.
If you want to compare to artisans - they were stressing about details that customers see, details that customers don’t see were to cut corners on.

Making fuss about indentation in code file is not artisanal. It is insane weirdo if we are charitable and if not clueless and childish.

Everyone wants a particular style. Except when they have to use someone elses style.

Pick a style stick with it. Review it every 6 months to year to see if anything needs to be tweaked.

If you hear 'we are professionals' you are about to see code that has 20 different styles and design patterns.

I worked with one guy who could not make up his mind and changed the whole style guide about every 2-3 weeks. It royal made him mad the original style guide fit on a couple of postit notes. Me and two other engineers bashed it out in a 1-2 hour meeting at the start of the project (odd number of people to vote on anything). It came down to the fact he came in after the fact and had no say in it. Then proceeded to change everything. One week it was tabs everywhere then spaces then tabs again. One day camel case, week later all lower, another partial hungarian, upper on random things, etc. Waste of time.

Ideally pick a style from a different large organization that you have no input in. Because the organization is large they will have put a lot of effort into it, but since you have no input you can just follow it without thinking. Sometimes an organization will make some really weird choices and you will be forced to change styles (google as rejected a lot of the latest C++ standard and thus their C++ style guide is not to be used elsewhere, but there are plenty of other good options).

Second best is to start a large cross company standards organization and only allow one representative per organization. Make sure there is a lot of process standing in the way of changes so that changes are only made when really justified (because most are not justified)

To be fair, for most developers code is what their customer is going to see.
they were stressing about details that customers see, details that customers don’t see were to cut corners on

Sure, but there's two differences between artisans and programmers.

Firstly, most artisans produce sellable products. Once the customer has bought an item, they would never see it again. I'm pretty sure that if there was a minor error on a self-produced table or a vase and it was standing in the artisan's own living room, they'd not be able to unsee it, and still work to correct it.

Secondly and more importantly: code is not just the product that programmers work on, it's also the workshop that programmers work in. And you bet your ass that artisans are very anal about the layout and organization of their workshop. Put away screws in the wrong box, or throw all the dowels of multiple sizes in the same container? The carpenter will fire his apprentice if it happens more than once; place your knives in the wrong place in a kitchen and the chef will eat you alive; not properly wearing or storing safety equipment can be a fireable offense in many places.

To me, a code review is how you close your workshop for the week: tools are cleaned and stored, floors are tidy enough to walk around, and the work area is available so I can come back on monday and be productive again. I shouldn't have to spend monday cleaning glass shards because someone left a hammer standing straight up on a glass table - or chasing down last week's lunch because someone left the fridge open and now the cheese has grown legs.

So no -- making fuss about code style and quality can certainly be artisanal (maybe not about indents specifically, but can certainly be about textual organization). Because the code is the workshop, and you know the next time you will enter this room it will be because of a high-priority demand and you can't afford to spend half your day cleaning up what you couldn't be bothered to do last time.

Have you ever noticed that anybody driving slower than you is an idiot, and anyone going faster than you is a maniac?
The old Carlin bit, was going to mention. :-D. I think he used, “asshole.”
"If you met an asshole in the morning, you met an asshole. If you meet assholes all day, you're the asshole." (or, something close to that)
Oh thank god, I thought that was just me.
Thing is, you can care for the craft, but let the code style and linting tools do what they do best and don't stress over them. Code reviews are better now that there's tooling that automatically checks for, fixes, or marks common issues like code style so the reviewer doesn't have to do anything with them.

That is, I'd argue the "stressing" is not about what these tools check, but about the tools and their configuration itself. Just go with all the defaults and focus on bigger issues.

Sure some people that care about minutiae are code artisans but what I have seen more often is co-workers weaponizing these discussions to hide their own incompetence.

I have seen so many people going on and on about best practices and coding styles and whatnot and using big words just in hopes to keep discussions going so no one figures out out that they don't know how to code.

When people stress over the details I care, it's craftmanship. When they stress over the ones I don't care, it's nitpicking.
To reduce your argument to its essence, you're saying typesetting is part of the craft of writing. I've yet to meet an author who believes this (other than enjoying editing their own work as output from a typewriter), and I think the same broadly applies to code. It's not that everyone thinks these things are unimportant, it's that caring deeply about doing them a particular way is orthogonal to the craft. It's something that has long been lampooned (tabs vs. spaces, braces, etc.) as weird behavior.
I think there are accessibility aspects to formatting. Specifically to different formatting.

Not sure the typesetting analogy is the best, but typesetting absolutely matters for readability. Authors don’t need to care about it because typesetting is easy to change (before printing) and because publishers spend time caring about it —- all before it ends up in the hands of readers.

More than one writer refuses to use a computer, preferring typewriters. Harlan Ellison learned how to repair typewriters after he could no longer find anyone to fix his. Stephen King wrote Dreamcatcher with a fountain pen.

Authors totally obsess over details that seem irrelevant to people outside that craft.

And that’s totally fine! But there is no correlation whatsoever between writing on a typewriter or using a fountain pen (or the physical experience of writing generally) and the quality of the writing. None.

There is nothing to support this in either writing or programming, at all. For every software craftsman out there obsessing over formatting, editor layout, linters, line length, there is an ancient, horrifyingly laid out C codebase that expertly runs and captures the essence of its domain, serves real traffic and makes real money.

Make your editor work how you like, but if my team lead started to get annoyed after my 4th formatting-only PR I should probably start to think about what I want them to bring up to my manager in my performance review.

I don't disagree with the broader point I just think authors aren't a good example for a non-tool obsessed group of people.

I read https://prog21.dadgum.com/ somewhat regularly when I think I am getting to tool obsessed.

> But there is no correlation whatsoever between writing on a typewriter or using a fountain pen (or the physical experience of writing generally) and the quality of the writing

I heard neal stephenson say that he writes using a pen rather than a wordprocessor specifically because it does affect the quality of the writing. Because he handwrites slower than he types, he does more thinking and editing in his head rather than after it's on paper.

And if that works for him then he should do that, and I have no problem with that at all. I just don't think there is anything one can say about writing tools that _generalizes_ to writing quality, and the same applies to the type of conversations programmers often engage in like "functions need to be short!" or "line length MUST be less than 90 or else I will reject this PR!" or "dark mode is objectively better to write code in" etc.

I have no problem whatsoever with people having preferences, I just think people mistake preferences for proof.

> There is nothing to support this in either writing or programming, at all.

There is no way this is true. There;s 100% chance that no human is productive is uglified JS code.

Personally, I modified a Hollerith reader to accept Jacquard tapes that I cut by hand with a scalpel.

I’m so much more productive now.

There is a class of people who refuse to see computer programming as an art.

They try to shoehorn it into being an engineering discipline and comparing it to authoring a book (something you can't give timelines on or T-Shirt size) probably horrifies them.

Ah, the joys of overloading. Do you mean "art" as the high-brow stuff we see in galleries and are produced in volumes of dozens per artist-years? Or do you mean "art" as the more common stuff that's produced by artisans are the rate of dozens per week? Because to me it's more the latter -- I'm an artisan, not an artist.
Those are tools you use to write, not typesetting. It's equivalent to wanting to code on paper or use a specific editor. Cognitive connection to tools is a real thing, and I know a number of authors who really can't form a mental connection with their writing if not using their tool of choice.

That doesn't mean it's normal for them only use a certain typewriter because of its typeface, insist a publisher use Garamond to typeset their book for publication, or refuse to write without a certain margin.

To bring it closer to programming, in collaborative writing especially (think manual writing at large corporations), nobody is insisting that everyone indents paragraphs their way because it's better. As long as there's consistency those matters are best left to the printer. When I was younger I knew a lot of technical writers who in fact really disliked the move to Word from traditional word processors, because they didn't want to be distracted by those things.

If typesetting and a grammar mistake in one sentence were what made a book viable or not, authors would care. I've seen enough (crazy expensive) bugs that could have been caught by linters and bugs introduced through insane formatting and style choices that I can't agree that a book and software are all that comparable.

I'm on team "agree at the beginning and then make it part of CI" and I basically never have to have this conversation more than once or twice per project now but I also think that the people most obsessed with it and dwell on it for their personal daily work are problematic, as are the people who hate any rules whatsoever and want to write complete shit code to just call the job done because "that's the important part".

The prose writing metaphor also falls apart the moment one admits that prose doesn't have the need (and is actually very terrible at) working collaboratively, concurrently but not perfectly synchronized and continuously on the same body of text, ensuring at the same time that combined changes don't add up to unwanted/wrong semantics, even in the long term. Are consistent indentations, variable names etc. strictly required for that? No, not logically, but the real world in which our software must be built is resource constrained, so every minute I spend parsing weird formatting inconsistencies is one minute less I can focus on the actual problem that needs solving. Just use a formatter/linter everyone. And I promise I don't care how it's configured, as long as it's consistent across the codebase
Plenty of writers work collaboratively, fyi. Even fiction authors like my mother routinely deal with multiple drafts and edits from multiple editors who review and suggest changes, from peer authors to professional editors contracted by the publishing house. And non-fiction authors routinely collaborate, too. I personally know some consequential non-fiction books where another author ghost wrote troublesome sections, not taking credit except in private.

And this ignores the collaborative writing many authors do to pay the bills: technical writing at large corporations, like bank manuals and such, or academic writing at universities. While consistency and standards are enforced, nobody's arguing that everyone else should really indent paragraphs their way, because that's the best way.

Type setting is not a good analogy here.

A better one would probably be accounting and spread sheets. Having common formatting conventions between spreadsheets (and code files) allows your brain to filter out the noise better. Obviously you can get too down in the weeds on "what are the best conventions" but the most important part is to have them and stick to them.

The purpose of writing is to produce something to be read, and typesetting is an important part of making a document readable.

It is incredibly irritating if I need to reformat code to be able to read it clearly before modifying it, then have to either back out all of the formatting changes to create a clean PR that shows the actual change, or create a PR full of formatting changes with the actual logic change buried somewhere within.

Most of these things really don't matter, and it all just boils down to "that's not how I would have written it". Well, okay, but that doesn't mean that's somehow objectively better.
No, focusing on code style is weird. It’s tablestakes it should be uniform in style and legible. Automate styling to vanilla defaults. Leave it.

It’s like obsessing over the font on the packaging of the marker you use to write to the whiteboard in a math seminar.

Now, weird is beautiful but it’s not usefull and mostly irritating in a professinal context.

Cherish your weirdness and celebrate when you can find people of same persuasion. But don’t bother your colleagues with it.

I’m saying this as a person with very deep weird personal interests as several here do - and I’ve learned this slowly and painfully.

If what you care about deeply can be automated by a linter, it's trivial, and you ought to just setup the linter rules, and go use all that time you just gained to work on something more meaningful.
Related: Anyone who's driving slower than I am is an idiot and anyone who's driving faster than I am is a maniac.
> Revered artisans are precisely the ones who care for the details.

Well yea, but which detail you care about still matters and reveals a lot about your "craft". Code style is of such little consequence compared to semantics it's always a little eyebrow-raising to see people who are extremely opinionated about it.

I've also noticed that this sort of thing can sort of fade into the background after a couple decades of coding. I know people who go on and on about how "beautiful" code is—to me it's just syntax serving a purpose. Sometimes you can make really elegant code that works well with the language, sometimes you can't. But how the code is presented impacts my reading comprehension very little unless you're doing something very strange (looking at you, early 90s c code with one-letter variable names and heavy macro usage).

Actually, I recant one element of this—Javascript and Typescript are just straight ugly and hard to read.

Somewhat agree and disagree. I bucket people's style into two camps, stressing over the former is largely unproductive, but stressing over the latter is crucial to writing high-quality, maintainable software.

Someone's style can be "different" without being "bad", and you have two basic options to deal with it. One is to authoritatively remove the soul via process (auto-formatters, code review, and to a lesser degree linters, etc. are all designed to create uniformity at the cost of individuality.) The other is to suck it up and deal with it, as this is just an inevitability of creating a team size larger than one: people have different tastes and those have to be reconciled. I somewhat prefer allowing for individuality, and individuals should endeavor to match the style of whatever module they're working in, out of courtesy to its owners/stakeholders if nothing else. However I have only worked independently or on small teams. Most large teams (/ open source projects) have gone the former route of automating all the fun/craftsmanship out of their systems, and even I think that makes sense at a certain scale.

Someone's style can just be objectively "bad", however, and I usually find it's evidence they just don't care about the source artifact that much, and they're focused on the results. (It can also be a sign of an under-performer that spends so much mental capacity just getting the code to work that they have no spare cycles to spend thinking about matters of taste.) If it compiles / works / passes the test-suite that's "good enough" and "their job is done" and they move on to the next task. These people tend to be hyper-literal thinkers that are very micro-task oriented: they see implementing a new feature as a checklist to be conquered, rather than being systems-level thinkers on a journey of discovery & understanding.

If the author is talking about the latter, I have to agree with you that the latter are quite difficult for me to work with; particularly since I know that the source has to be maintained & supported over a much larger time-scale. The source-code is like your house, you live in it, being comfortable to work with/in/on it is the key to success. The deployed artifact may live for only a few weeks, days, or even hours before it gets replaced. The source has evolved over decades. You (the organization) are practically married to it. To further the analogy: I don't mind if somebody wants to hang posters in their room for a band I don't like. (Hell I can even handle if a group of those posters are tastefully hung out-of-level to make some kind of statement.) I do mind if their furniture is blocking a vent, the outlet covers are hanging off, there's a hole in one of the walls, a light has been burnt out for months, and the window-blinds over there are clearly broken but they insist it's fine because daylight still gets through.

Maybe I'm projecting my own views here but I interpreted those two statements as being about different things: the finished product vs the process that gets you there.

I care deeply about the end result that is presented to the user who has no idea what code even looks like. How we put together the UI. How we load data to minimize delays. That's "the craft" to me.

I care much less about code style, linting etc. that no-one other than a small group of developers will ever see. To a certain extent the latter enables the former. But I've often witnessed the latter being valued over the former and that's where things start to go wrong.

In some ways, it's the point OC makes — that it's subjective. It's a culture problem.

In our profession, our conventional approach to resolve these kinds of differences is to reduce them to a specific set of conditionally applied rules that everyone _has_ to agree on. Differences in opinions are treated as based on top of a more fundamental set of values that _have_ to be universal, modular, and distinct. Why do we do this? Because that's how we culturally approach problem-solving.

Most industries at large train and groom people to absorb structured value systems whose primary function is to promote productivity (as in, delivery of results). That value system, however, ultimately benefits capital most, not necessarily knowledge or completeness.

Roles and positions ultimately encompass and package a set of values and expectations. So, we are left with a small group of people who practiced valuing few other aspects but feel isolated and burdened with having to voluntarily take on additional work (because they really care about it), and others unnecessarily pressured to mass-adopt values and also burdened taking on what feels like additional work that only a small group of people like to care about.

In the cultural discourse, we are trying to fix minimum thresholds of some values and value systems and, correspondingly, their expectations. That is never going to be possible. In and of itself, that can be a valid ask. However, time and resources are limited, and values are a continuum. Fixing one requires compromising on another. This is where we are as a professional culture and community in the larger society today.

The Tech industry refuses to break down the role of a "software engineer/developer" further than what it is today and, consequently, refuses to further break down more complex/ambiguous values and value systems into simpler ones, thus reducing the compromises encompassed in and perceived by different sub-groups and increasing overall satisfaction of developers in the industry. Instead, we've expanded on what software developers should be responsible for, which has caused more and more people to burn out trying to meet a broader set of expectations and a diminished set of value systems with more compromises to accommodate that.

Ideally, we need an industry and a professional culture that allows for and respects niche values and acknowledges the necessity of more niche roles to focus on different parts of the larger craft of software development.

PS. As a side note, the phrasing of it in the article is unfair, which OC is pointing out too — there is a false equivalency drawn between "caring for the craft" and "stressing over minutia." This causes, in this context of having a discourse around the article, those who value and want to talk about the value of caring for the craft to be viewed and perceived as the insane weirdos who stress over the minutia that the author was referring to.

It is a fine balance, and everyone that doesn't "drive the same speed" as me, frustrates me.
People, though, tend to spiral down into the bikeshedding abyss. It’s one thing to stand your ground about a linting rule that has proven effective in combatting certain classes of errors that you encountered in the field. It’s another thing to make every discussion be about linting rules.

I cannot put it into words right, but you can see there’s usually a vibe. It will be different coming from an experienced developer knowing what they are talking about from lived experience, and from a clueless one who has seen shit but is hell bent on process and rules.

I love that the top comment is a 10 level deep bikeshed about bikeshedding.
tabs or spaces are ok. Mix of tabs and spaces is not.
> What you’re essentially saying is “cherish the people who care up to the level I personally and subjectively think is right, and dismiss everyone who cares more as insane weirdos who cannot prioritise”.

Theses are bullet points expressing general rules of thumb, not legal treatises. You're reading far too much into these.

I dont care about style as long as you can give me a document that specifies all the rules. It can be a simple text file, but don't expect me to remember them all from one convo.

At least with Python I just push everyone to follow PEP-8 makes it easier.

>Revered artisans

This may point to the dividing line. Software is required to be functional, not simply "artistic". You can certainly make the argument that there are non-functional considerations in its construction: readability, maintainability, extensibility, etc. And, these absolutely intersect with style, so it's tempting to apply words like "artistry".

But, is there a point where details veer into personal preference and insistence on style for the sake of style? I think so, and many of us have seen this. For those who haven't yet, stick around!

"Have you ever noticed that anybody driving slower than you is an idiot, and anyone going faster than you is a maniac?" -- George Carlin
Reminds me of the equivalent you see so often about driving where people slower than you are morons, while those faster than you are crazy.
Formatting is also important for cohesion. A code base that is formatted is easier to browse.

And I couldn't care less about where you put the {}. I love autoformatters.

Linting is a little more. It's both style and genuine issues, like don't == instead of === in JavaScript.

I think most people who care about code have a bit of OCD over these things, but there is a difference between how the code looks vs. how it is structured. I think that's what the author means.
I care about the craft of constructing programs. I do not care about the craft of typesetting them.
> What you’re essentially saying is “cherish the people who care up to the level I personally and subjectively think is right

No. The two aspects (let's call them the craft and the minutiae) are orthogonal, they're not different level of caring about the same thing. I've seen people obsessed over minutiae and writing buggy, careless and unmaintainable code.

As with other formal aspects (e.g., testing) the quality of the code and the level of adherence to convention or forms are independent of each other; but they do both consume the same limited resource, which is the time and attention of developers.

The idea that style and linting define the “craft” is bizarre. What it sounds like you are saying is that you prefer style over substance. Not a single developer I’ve ever revered cared about styling. It’s a means to an end for large teams because to make it easy to read you need commonality.

It’s kind of like what Bruce Lee said about punches. Before you know how to code, styling is just styling. When you become proficient in coding, styling is more than just styling. But when you’ve mastered coding, styling is just styling again. Be like water my friend.

I took this as "don't stress about things you can automate."

Yes, you need to care about this. But for the most part you should just follow the conventions of the language/framework, and not reinvent the wheel. Instead, you should put cycles into crafting architectures and algorithms.

The trick to life is finding the exact right amount of fucks to give.
Code style and linting rules are not something you obsess over. They are things you set once and then follow more or less automatically.
Code is human readable text. Authors should craft it with care.

The linter people are hypercorrectionalists of the type that would change “to boldly go where no man has gone before!” because it’s a split infinitive.

Equivalently: code is written for other humans to read.
There’s a big difference when someone stresses over minutiae and fails to see the bigger picture or why the overall design might be flawed. Wanting to have code up to a given standard is a laudable goal but as with everything, it can be taken to such an extreme that people lose sight of the real value being delivered. Code is not the end goal, except for some people it is.
I could agree with your general sentiment, but don't in this case. There's nothing in code style that is important for the craft, at least not in the way it's usually discussed. It could be important if one style is somehow dangerous, but for most people it's a matter of aesthetics, which after a (too) long time doing this I think is weird. Because there are so many real problems to deal with, and where to place a character certainly ain't one.
There are aspects of code that matter and aspects that don't. I lump everything that doesn't matter under "code style", and cherish those who cherish the remaining aspects.
Having a consistent code style and adhering to a set of linting rules is important, but that they exist is far more important than their particulars. I get bored very fast when people argue about the visual presentation of their code. Use a formatter and be done with it. Same with linting rules; pick a set that your team can work with. If you dial everything up to 11, you’re going to have perfectly compliant code that doesn’t do anything. Recognize that compliance sometimes makes your code worse and pick your roles accordingly.
No, they are saying that code style, minting rules, and other minutia are not the essence of the craft. Which I agree with.

You have to read between the lines to pick that up though, so your criticism seems valid.

> Revered artisans are precisely the ones who care for the details.

Abhored obstructionists and covert saboteurs are also the ones that care for the details.

You need to care about right kind of details. Any specific linting is not that kind. Linting at all is.

Artisan without a proper sense could spend weeks exquisitely decorating a spoon with gaping hole in the middle. State of nearly all software is more or less horrible. Choosing the right linting rules is most often than not putting lipstick on a pig.

I have a lot of sympathy for the golang auto-linter here.

I can also understand the fuss better in indentation-is-structure languages like python.

Now we have tooling to make sure (1) code style is consistent and (2) you don't have to stress about it.

Every language has automatic formatters. Use them, configure them if you don't like the default (in accordance with your team), and configure your editor so that is applied automatically when you save. And use CI to detect PRs with bad formatting so devs who don't have configured their editor yet can't break it.

Same with linters, you still have to agree with your co-workers about which rules make sense to be enforced, but with good editor integration you can see it right away and fix it as you code.

That's my stance--formatting and style is important but it's also a job for machines. I think you can still quibble over formatting but the result should be automation, not nit-picky code review comments or chat war threads.
Whether one uses tabs or spaces, Allman or K&R, etc. is largely immaterial.

On your own projects, choose a style, go with it. On someone else's project, go with the style chosen. On a shared project, come up with a style, go with it.

Code _organization_ matters way more than code _style_. Where style refers only to the aesthetic choices that don't impact the structure or flow.

This is baldfaced relativism. It only takes a moment of reflection to understand the absurd consequences of this position. For example, how it becomes meaningless to speak of caring about the craft if there is no objective definition of what exactly one should care about, or what is important. It ceases to have intersubjective relevance.

> What you call “stressing over minutiae” others might call “caring for the craft”.

So what? The presence of disagreement is not an argument in favor of relativism and subjectivism. People can be wrong, and they can be wrong about what is valuable. Value is not subjective. The fact-value dichotomy is false.

That's the general principle. As far as this particular example is concerned, the author didn't say things like code style and linting rules have absolutely no value. They have some value. The question is how much, especially in the grand scheme of things, and whether one's concerns, attention, and efforts are proportioned to the objective value of such things. That's how this question should be framed. The author's position, charitably read, is that it is objectively irrational to obsess over such things.

If you wish to rebut, then go ahead and provide an argument, but don't retreat into the bollocks of subjectivism.

Sigh... it really doesn't matter compared to say how and what you test, and that you are consistent. He's saying your opinion about where a brace goes or even spaces or tabs is just not that important compared to crafting simple systems with clear code.
> People who stress over code style, linting rules, or other minutia remain insane weirdos to me.

Until you open a file that has 10 different coding styles from 5 different developers. Just the variations of variable naming schemes alone in individual code files that I see/edit, would drive anyone crazy.

It's a strange assertion to claim that people who care about formatting care about the craft more. IMO, it's the opposite: people who care about formatting prefer to talk and think about the periphery of programming not the actual act of programming itself. For the record, I reject the usually offered claim that the most important attribute is consistency. I have an easier to reading code written in a different style than understanding somebody with a different accent, but nobody is claiming we should only hire teams from the same regions of the same country.
Didn't George Carlin say that everyone who drove faster than one was a maniac and everyone slower was a moron?
I actually disagree with about half of this.

> Typed languages are essential on teams with mixed experience levels

Essential, meaning 'cannot exist without', it's not. I've seen this work a number of places.

> Blind devotion to functional is dumb.

Managing/limiting state is always a worthwhile pursuit. I'd think he would agree since he seems to value simplicity

> People who stress over code style, linting rules, or other minutia remain insane weirdos to me. Focus on more important things.

That's like saying don't check if your cars tires still have any pattern left, the engine is more important. Until it isn't. Streamlining the smaller details allows the brain to focus on more important stuff, but the details matter and their value increases exponentially with the size of the project.

Regarding code style etc. I get where he's coming from as plenty of people made entire careers on pursuing the perfect linter configuration. They're a net negative in every project.
I'd love some clarification if he meant people who bike shed or people who care to setup a linter and formatter as part of their workflow.
Your rebuttals are all agreeing with him...

I don't think you're grasping the nuance he's putting out there. Instead you're choosing to be overly pedantic over the word essential.

> People who stress over code style, linting rules, or other minutia remain insane weirdos to me. Focus on more important things.

Could not agree more.

I agree but have been on teams where this slows down PR review or arguments break out. Delegating to the linter and format on save can get the team past this.
Those teams have these weirdos the article talks about.
After almost 40 years, between hobby coding, education, and professional experience, kind of share a similar view in a couple of topics.

Additionally such an extend experience gives us another point of view on trendy subjects that are experiences from the past newly repacked.

And desilusion, seeing so many great ideas taking decades to become relevant, or never at all.

I strongly agree with almost all of this except the last part about project managers - a good one is invaluable, an ok one can still be helpful. The majority probably are neither (and their ability to be good at managing projects depends a lot on other org functions - not necessarily within their control) but way better than 90%+ that the author suggests.
No, objects aren't generally 'good', unless you think keeping multiple state machines in sync is 'good'.

OO is not evil, but it also shouldn't be your default solution to everything.

Also, who is this person? I immediately distrust someone who calls themselves 'a pretty cool guy'. That's for the rest of us to decide.

I had a different read of that point. More along the lines of “don’t throw the baby out with the bath water” (might have butchered that saying?).

I’m also more in the FP camp - even wrote a book on the topic of FP. But I also acknowledge OO is not inherently a bad choice for a project, and many languages nowadays do exist along a spectrum of OO and FP rather than being strictly one of the other.

To me a benefit for OO might be the ubiquity - you can generally assume people will understand an OO codebase if they have done a few years of coding. With more strict FP that is just not a given - even if people took a Haskell course in Uni a decade ago :).

The way I like to use OO (usually not real OO, but rather class-based languages) is to minimize its mutable state. Often mutability is merely a lack of using builder patterns. Some state can be useful as long as it's easy and makes sense to globally reset or control. It's like writing a process as a construction of monads before any data is passed into it. Similarly a tree of processing objects can be assembled before running it on the input.
Exactly. Automatically adding getters and (especially) setters to a class is something I see far too often.
I begrudgingly have had to enter the world of Spring Boot over the last couple years, and this drives me nuts. Every damn thing needs getters and setters so that the ten thousand magic Spring annotations can mutate things all over the place. If the business logic is complex, I try to make immutable domain models separate from the models that have to interact with Spring, but that can require a lot of boilerplate.
Agree on both of your takes.

I've probably written more Java than any other programming language during my career, and I've seen both good and bad ways of writing Java.

Bad java heavily uses lots of inheritance, seems to think little about minimizing exposed state, and (unrelated to this discussion) uses lots techniques I like to call 'hidden coupling' (where there are dependencies, but you can't see them through code, as there is runtime magic hooking things up).

Good java almost never uses inheritance (instead composes shared pieces to create variation on a theme), prevents mutability wherever possible, and makes any coupling explicit for all to see.

Good java still has classes and objects, but starts to look pretty 'functional'.

> No, objects aren't generally 'good', unless you think keeping multiple state machines in sync is 'good'.

Sure, if you disregard good object design, like the single responsibility principle and using mutability only when really needed.

> OO is not evil, but it also shouldn't be your default solution to everything.

With Smalltalk and Objective-C both being effectively dead at this point, that really only leaves Ruby (and arguably Erlang) as the only languages that are able to express OO. And neither of those languages are terribly popular either. Chances are it won't be your default solution, even if you want it to be.

J ... J ... Java.
Patrick Naughton came from the Smalltalk world, so Java is definitely inspired by Smalltalk, but he didn't bring along the oriented bits. Its object model is a lot closer to C++'s. To have objects does not imply orientation.
“When I use a word,” Humpty Dumpty said in rather a scornful tone, “it means just what I choose it to mean—neither more nor less.”
"I made up the term object-oriented, and I can tell you I did not have C++ in mind.", said Alan Kay.
That was the easy laughter set-up line. The follow-up seemed to confound the audience.

What was the follow-up?

> and arguably Erlang

Curious about your case for this. I don't know a lot about Erlang other than "it's what Elixir is based on" or whatever technical jargon is more accurate. I thought it was functional.

> Curious about your case for this.

I was mostly riffing on the time Joe Armstrong, creator of Erlang, said that Erlang might be the only object-oriented language in existence. Although he's not exactly wrong, is he?

> I thought it was functional.

I think that is reasonable. Objects, describing encapsulation of data, are what define functional. Without encapsulation, you merely have procedural. Of course, that still does not imply the objects are oriented...

For that you need message passing. But Erlang has message passing too! So there is a good case to be made that is object-oriented.

> Frontend development is a nightmare world of Kafkaesque awfulness I no longer enjoy

My feeling is that a lot of negativity towards the frontend stems from assuming that the entire field is like React and its community. It's really not like that.

I think Frontend (or maybe slightly expanded App Development) is a greatly underappreciated skill. It's easy to throw a UI together, but to do it in a way that is obvious to your user's, can be maintained and can move at the speed the business needs is a tough ask.
I agree, getting all the pieces together and having it be maintainable and enjoyable to work on is part of the craft.

I enjoy the challenge, and have found a great niche for myself doing exactly that.

The much bigger problem is that you not only have to deal with the technical challenges, but also the visual design challenges where non-technical people have all kinds of opinions that you have to deal with. Things get really nasty once you have to implement features that emulate non-web functionality like right-click menus and the UI becomes an inconsistent mess.
Visual Design Challenges is one I hear most frontend developer complain about. I explained a queen duck (https://bwiggs.com/notebook/queens-duck/) to one of them and he started doing that several times in his project. If you look at this git repo, there would be commits called "queen duck" right before he would demo so he could get feedback about the "mistake" he made, revert the commit and move on with his life.
> DynamoDB is a good database (IFF your workload lines up with what it's offering)

"The correct tool for the job."

Seeing a document database used for tabular purposes gives me a sad.

> ORMs are the devil in all languages and all implementations. Just write the damn SQL

It depends on what you're writing. I've seen enough projects writing raw SQL because of aversion to ORMs being bogged down in reinventing a lot of what ORMs offer. Like with other choices it is too often a premature optimization (for perf or DX) and a sign of prioritizing a sense of craftsmanship at the expense of the deliverables and the sanity of other team members.

It's not so much optimization but experience that on any sufficiently large project you gonna run into ORM limitation and end up with mix of ORM and direct queries. So might as well...
Starting with raw SQL is fun. But at some point you find out you need some caching here, then there, then you have a bunch of custom disconnected caches having bugs with invalidation. Then you need lazy loading and fetch graphs. Step by step you'll build your own (shitty) ORM.

Same thing for people claiming they don't need any frameworks.

> you find out you need some caching here, then there

Forgive my ignorance, but how do ORMs help with adding caching? Or are you implying they obviate or reduce the need for caching?

They do caching themselves so that some of your queries via ORM won’t hit the actual db.
So, given the main issue with ORM is the object/relational part... (https://web.archive.org/web/20160301022121/http://www.revisi...)

Why is caching not a feature in DB connection pools? I mean, most databases have it on their side, why not have it as an option for the same query sets prior to hitting the db, with configurable invalidations? Or is it, and I've just never thought to look for it.

Integrating cache into connection pools brings little added value since connection pools don't have enough context/information to manage the cache intelligently. You'd have to do all the hard work (like invalidation) yourself anyway.

Example: if you execute "UPDATE orders SET x = 5 WHERE id = 10", the connection pool has no idea what entries to invalidate. ORM knows that since it tracks all managed entities, understands their structure, identity.

I guess I was thinking more of frequently run queries against infrequently modified data or where stale data doesn't matter so much. The sort of things that are ideal cache targets. You'd think you could tag queries like that. Sort of the things that a CDN caches but more granular. Sure if it's stuff that's frequently changed, an ORM could reason about it just like, well, the database does, but then you're back into all the bad things about running your shadow database with a badly fitting model, and you'd be better off just ensuring all or part of the database ran closer to your app with replication, say, in memory on same server.
That would be a result set layer, not a connection pool. Could make sense if you worked with rows, but if you use ORM, why mapping cached row again and again? ORMs cache hydrated objects, which seems to be more efficient.
Yeah, it was more to see if there were any benefits to an ORM that could be used without the, well "ORM" part :)
JPA implementations have "managed entities", sometimes called session or 1st level cache which is making sure that every entity is loaded at max. one time within a transaction. Like e.g. checking user/user permissions is something which typically has to be done in several places in course of a single request - you don't want to keep loading them for every check, you don't want to keep passing them across 20 layers, so some form of caching is needed. JPA implementations do it for you automatically (assuming you're fine with transaction-scoped cache) since this is such a core concept to how JPA works (the fact it's also a cache is kind of secondary consequence). JPA implementations typically provide more advanced caching capabilities, caching query results, distributed cache (with proper invalidation) etc.
caching is orthogonal to using or not using ORM. You might opt to have caching with or without ORM in a consistent manner. You can also opt to add read replicas fronted by say pgcat in Postgres case without having separate caching layer.
I guess this is a point where terminology matters. If you work with SQL database in an OOP language, you pretty much always do some object-relational mapping, no matter if you have a big framework or just raw SQL connection.

But this is not what people usually call as ORMs. All the "bad kind of ORM" (JPA impls, Entity Framework, SQLAlchemy, Doctrine, Active Record...) have some concept of an entity session which is tracking the entities being processed. To me, this is a central feature of an ORM, one of its major benefits. It is, incidentally, also serving as a transaction-scoped cache.

I won't of course dispute that you can have caching on other levels as well (which may perform differently, for different use cases).

As SRE who dealt with more caching errors then I care to. Alot of caching comes down to YAGNI.

To his point: It's very hard to beat decades of RDBMS research and improvements

Your RDBMS internal caching will likely get you extremely far and speed difference of Redis vs RDBMS call is very unlikely to matter in your standard CRUD App.

> Redis vs RDBMS call is very unlikely to matter in your standard CRUD App

To any juniors reading: cache the response payload (or parts of it), not the results of database queries.

There are plenty of libraries/packages for SQL that do all of that for you, too. The choice isn't between a sophisticated ORM and just throwing SQL text at a socket. The fundamental assumption of ORMs is broken, but much of the tooling works well and exists in non-ORM places.
It seemed to me TOPLink had this figured out, and then hibernate took it all away.
Depends upon the ORM. Like all frameworks, a really good one is a significant productivity boost while a bad one is faworse than none at all.
And not just the ORM, but the way it's used. If you ensure that lazy-loading is turned off from day 1 and stays off, you might be okay. But if you don't pay attention to this and write a bunch of code for N years until all the "select N+1"s you've been unwittingly doing finally force your DB to a crawl... now you're in trouble.
It’s the devil I know and for most of my projects I’ll likely never forsake ORM for raw SQL.
One job I had, we got handed a code base with at least 4 different reinventions of an ORM in it.

It became clear that each developer who'd worked on the code had written their own helpers to avoid direct SQL. It took a fair bit of persuading leadership, but the first task ended up being doing a huge reactor of everything SQL. Unsurprisingly enough, lots of bugs got squashed that way.

I've never understood the ORM hate because a good ORM will get out of the way and let you write raw SQL when necessary while still offering all of the benefits you get out of an ORM when working with query results:

1. Mapping result rows back to objects, especially from joins where you will get back multiple rows per "object" that need to be collated.

2. Automatic handling of many-to-many relationships so you don't have to track which ids to add/remove from the join table yourself.

3. Identity mapping so if you query for the same object in different parts of your UI you always get the same underlying instance back.

4. Unit of work tracking so if you modify two properties of one object and one property of another the correct SQL is issued to only update those three particular columns.

5. Object change events so if you fetch a list of objects to display in the UI and some other part of your UI (or a background thread) add/updates/deletes an object, your list is automatically updated.

6. And finally in cases where your SQL is dynamic having a query builder is way cleaner than concatenating strings together.

For those who are against ORMs I am curious how you deal with these problems instead.

You are describing data mapper ORMs, a.k.a the good ORM. I think all the other ORM-loathing guys here had bad experiences with active record ORMs, a.k.a the bad ORM.

Also, infrastructure guys and DBA types tend not to like ORMs. But they are not the ones trying to manage the complexity in the business process. They just see our queries are not optimal, and it is everything to them.

Yes I suspect a lot of the ORM hate comes 1) from people using poorly designed ones or 2) from people working on projects that don't really require the features I mentioned. Like if you are generating reports that just issue a bunch of queries and then dump the results to the screen you probably don't care that much about the lifetime of what you've retrieved. But just because an ORM might not be the right tool for your project doesn't make it a bad tool overall, that would be like saying hammers are bad tools because they can't be used to screw in screws.
Oh was I enthusiastic when I first got my hands on an active record ORM: "I can use all my usual objects and it'll manage the SQL for me? Wow!". That enthusiasm reached rock bottom rather quickly as soon as I wanted to fine tune things. Turns out I'm not a fan of mutating hierarchical objects and then calling a magical .commit()-method on it, or worse: letting the ORM do it implicitly. That abstraction is just not for me and I'd rather get my hands "dirty" writing SQL, I guess.
Right! They should really be considered two different things. I've worked a lot with Django (the bad type) which people tend to love, but I've seen the horrors that it can produce. What they seem to love about it is being able to write ridiculously complicated SQL using ridiculously complicated Python. I don't get it. These types of ORMs don't even fully map to objects. The "objects" it gives you are nothing more than database rows, so it's all at the same abstraction level as SQL, but it just looks like Python. It's crazy.

SQLAlchemy is the real deal, but it's more difficult and people prefer easy.

> being bogged down in reinventing a lot of what ORMs offer.

There's a saying - if you hate ORMs and don't use them, eventually you're going to write your own ORM.

Just mentioned to an acquaintance a couple of days ago that Entity Framework is the biggest flip-flop of my personal career. I was a _rabid_ supporter when it was released a decade and a half or so ago, and the appeal has decreased linearly over time, to the present day.

Statically-typed queries written in a mini-DSL within your application language seems like such a joy!

But then the configuration is a hassle. The DbContext lifecycles are a hassle. The DbContext winds up a big ball of mutable state that often gets passed all up and down your call stack, reducing the ability to reason about much of the code locally. Was this instance initialized with or without change tracking? How many and what changes have been applied? Were these navigation properties lazily or eagerly loaded?

And it promises to keep your domain persistence ignorant with its fluent configuration syntax. But then you have compromise on that here, then compromise in it there.

Pretty soon, you realize that you started out building a project for domain X or domain Y, only to realize that you're trying to shoehorn domain X/Y behavior into your Entity Framework app.

I'm a hardliner on supporting this - I'd go further - all the DB code should be in SQL, with the database being manipulated by stored procedures and the db schema not even being exposed to the common developer.

As far as I know this is a very oldschool view on how to treat dbs, but I still think this is the only correct one.

I hate ORMs with a passion - they're just a potential source of bugs and performance issues, coming from either bugs in the engine, devs not understanding SQL, devs misjudging what query will get generated, leaky abstractions etc.

It's big enough of an ask to understand SQL itself, it's the height of folly to think you can understand SQL when it's being generated by some Rube-Goldberg SQL generator, especially if said generator advertises that you don't need to know SQL to use it.

You must live a charmed life if you haven't run into major problems with stored procedures.
> Most won't care about the craft. Cherish the ones that do, meet the rest where they are

This becomes easier when you've transitioned from someone who cares to someone who doesn't. Some of us burn out from the industry and fall out of love with the job.

I understand my curmudgeon-ly ex-colleagues a lot more nowadays.

Regarding scaling… one thing that drives me bonkers about Apache Superset is that the maintainers are absolutely adamant that it must be deployed with kubernetes because it’s meant to scale. The project works just fine with a simple and slim docker/compose deployment, which is easier for small teams to manage. The lead maintainers refuse to document this and have unnecessarily sprinkled the docs with warnings.