Live data from Hacker News

The Grug Brained Developer (2022)

grugbrain.dev

561–570 of 603 posts

Re: The Grug Brained Developer (2022)

#561
post #519

Earlier quoted context omitted.

> How many people actually write exhaustive tests for everything that could possibly be null? Of those who are concerned about type theory? 99%. With a delusional 1% thinking that a gimped type system (read: insufficient for formal proofs) is some kind of magic that negates the need to write tests, somehow not noticing that many of the lessons on how to write good tests come from those language ecosystems (e.g. Haske…

> to see how they ended up in that situation The "how" is almost always lack of discipline (or as I sometimes couch it, "imagination") but usually shit like https://github.com/microsoft/SynapseML/issues/405#:~:text=cl... I've had to learn in my career not to open other people's projects in a real IDE because of all the screaming it does about "value can be null"

> in a real IDE because of all the screaming it does about "value can be null"

Yeah, when you have extra tools like that it can certainly help. The thing is that you can ignore any warning! I like it to be a compiler error because then there's no way to put that on the tech-debt credit card and still pass CI. If you are able to put those warnings into your CI so a PR with them cannot merge, then that's like 99% of what I like in my code: make the computer think of the low-hanging-fruit things that can go wrong.

With all of that said, solving for null does not get you everything a tagged union type does, but that's a different story.

Re: The Grug Brained Developer (2022)

#562
post #560
post #536

Earlier quoted context omitted.

> A compiler would be even better. Right, but the condition here is that the languages that are expressive enough to negate the need for testing are, shall we say, unusable. In the real world people are going to be using, at best, languages with gimped type systems that still require testing to fill in the gaps. Given that, we're trying to understand your rejection of the premise that the tests you will write to fill…

In the example you gave you have an incomplete implementation of len. We had either a language extension or a compiler flag to disallow incomplete implementations in Haskell (pretty sure it's the flag -Werror), and Elm has no way of allowing them in the first place. I should have specified that that was the case, because "Haskell" is a rather broad term since you can turn on/off language extensions on a per file basi…

> We had either a language extension or a compiler flag to disallow incomplete implementations in Haskell

"Better flag choices would have caught it" is a poor take given what came before. Of course that's true, but the same as your "better tests would have caught it". However, that really has nothing to do with our discussion.

Again, the premise here is that you are writing tests to close the gaps where the type system is insufficient to describe the full program. Of course you are as the astute observation of "My hair is too shiny and my muscles are too large to have to deal with [error prone things] everywhere. Can't do it." is true and valid. Null checks alone, or even all type checks alone, are not sufficient to satisfy all cases of [error prone things]. That is at least outside of formal proof languages, but we already established that we aren't talking about those.

So... Given the tests you are writing to fill in those gaps (again, not tests specifically looking for null pointer cases, but the normal tests you are writing), how would null pointer cases slip through, even if the compiler didn't notice? What is the concrete example that demonstrates how that is possible?

Because frankly I have no idea how it could be possible and I am starting to think your rejection was just made up on the spot and thrown out there without you giving any thought, or perhaps reiterating some made up nonsense you read elsewhere without considering if it were valid? It is seemingly telling that every attempt to dismiss that idea has devolved into bizarre statements along the lines of "well, if you don't write tests then null pointer exceptions might make it into production" even though it is clear that's not what we are talking about.

Re: The Grug Brained Developer (2022)

#563

Earlier quoted context omitted.

An implementor of a data structure might take precautions for users of the data structure to perform such visiting operations by passing in a visitor-like thing.

I just don't think it's a significantly better way of dealing w/the problem than a recursive function that dispatches on the arg type (or whatever) using an if statement or pattern matching or whatever. The additional complexity doesn't add significant value IMO. I admit that's a subjective claim.

I mean, at some point you can also make that recursive function take an argument, that decides what to do depending on the type of the item, to make that recursive function reusable, if one has multiple use-cases ... but that's basically the same as the visitor pattern. There really isn't much to it, other than programming language limitations, that necessitate a special implementation, "making it a thing". Like when Java didn't have lambdas and one needed to make visitors objects, ergo had to write a class for them.

Re: The Grug Brained Developer (2022)

#564

“Good debugger worth weight in shiny rocks, in fact also more” I’ve spent time at small startups and on “elite” big tech teams, and I’m usually the only one on my team using a debugger. Almost everyone in the real world (at least in web tech) seems to do print statement debugging. I have tried and failed to get others interested in using my workflow. I generally agree that it’s the best way to start understanding a s…

There was a good discussion on this topic years ago [0]. The top comment shares this quote from Brian Kernighan and Rob Pike, neither of whom I'd call a young grug: > As personal choice, we tend not to use debuggers beyond getting a stack trace or the value of a variable or two. One reason is that it is easy to get lost in details of complicated data structures and control flow; we find stepping through a program les…

One thing this quote doesn't touch is that speed of fixing the bug isn't the only variable. The learning along the way is at least as important, if not more so. Reading and understanding the code serves the developer better long term if they are regularly working on it. On the other hand debuggers really shine when jumping into a project to help out and don't have or need a good understanding of the code base.

Re: The Grug Brained Developer (2022)

#565
post #211

Earlier quoted context omitted.

It's absolutely not an anti-pattern if you have appropriate tools to handle different levels of logging, and especially not if you can filter debug output by area. You touch on this, but it's a bit strange to me that the default case is assumed to be "all logs all the time". I usually roll my own wrapper around an existing logging package, but https://www.npmjs.com/package/debug is a good example of what life can be…

I know a lot of people do that in all kinds of software (especially enterprise), still, I can't help but notice this is getting close to Greenspunning[0] territory. What you describe is leaving around hand-rolled instrumentation code that conditionally executes expensive reporting actions, which you can toggle on demand between executions. Thing is, this is already all done automatically for you[1] - all you need is…

A logging library is very, very far from a Turing complete language, so no Greenspunning. (Yes, I know about that Java logger fiasco from a few years ago. Not my idea.)

I don't want logging done automatically for me, what I want is too idiosyncratic. While I will log every call on major interfaces, I do want to control exactly what is printed. Maybe some parameter values are not of interest. Maybe I want special formatting. Maybe I want the same log line to include something computed inside the function. Also, most of my logging is not on entry/exit. It's deeper down, to look at very specific things.

Look, I do not want a debugger, except for tiny programs, or debugging unit tests. In a system with lots of processes, running on lots of nodes, if a debugger is even possible to use, it is just too much of a PITA, and provides far too miniscule a view of things. I don't want to deal with running to just before the failure, repeatedly, resetting the environment on each attempt, blah, blah, blah. It's a ridiculous way to debug a large and complex system.

What a debugger can do, that is harder with logging, is to explore arbitrary code. If I chase a problem into a part of my system that doesn't have logging, okay, I add some logging, and keep it there. That's a good investment in the future. (This new logging is probably at a detailed level like DEBUG, and therefore only used on demand. Obvious, but it seems like a necessary thing to point out in this conversation.)

Re: The Grug Brained Developer (2022)

#566

Earlier quoted context omitted.

I keep trying to explain this to tiny dev teams (1-2 people) that will cheerfully take a trivial web app with maybe five forms and split it up into “microservices” that share a database, an API Management layer, a queue for batch jobs to process “huge” volumes (megabytes) of data, an email notification system, an observablity platform (bespoke!) and then… and then… turn the trivial web forms into a SPA app because “t…

The only useful definition of a "service" I've ever heard is that it's a database. Doesn't matter what the jobs and network calls are. One job with two DBs is two services, one DB shared by two jobs is one service. We once had 10 teams sharing one DB, and for all intents and purposes, that was one huge service (a disaster too).

A more precise view is that there are boundaries inside of which certain operations are atomic. To make this more precise: the difference between a “dedicated schema” and a “database” is that the latter is the boundary for transactions and disaster recovery rollbacks.

If you mix in two services into a single database - no matter how good the logical and security isolation is — they will roll back their transactions together if the DBA presses the restore button.

Similarly they have the option (but not the obligation!) to participate in truly atomic transactions instead of distributed transactions. If this is externally observable then this tight coupling means they can no longer be treated as separate apps.

Many architects will just draw directed arrows on diagrams, not realising that any time two icons point at the same icon it often joins them into a single system where none of the parts are functional without all of the others.

Re: The Grug Brained Developer (2022)

#567

Earlier quoted context omitted.

But why would users of the module care about the dependency packages? You could still have a module with only a few methods and that's the interface.

If “everyone would just” restrict themselves to import only the package you meant to public API, sure, it would work. But everyone will not just.

I'm not sure why you would bother, though. If you need the package, just import it directly, no? (besides, in many languages you can't even do that kind of thing)

Re: The Grug Brained Developer (2022)

#568

Earlier quoted context omitted.

> ... I disagree with two things: the assumption of incompetence on the part of geophile to not make logging conditional in some way ... I assumed nothing of the sort. What I did was identify an anti-pattern and describe an alternative which experience has shown to be a better approach. "Incompetence" is your word, not mine. > ... and adding the label of "anti-pattern" to something that's evidently got so much nuance…

You are very attached to this "voluminous" point. What do you mean by it? As I said, responding to another comment of yours, a distributed system I worked on produced a few GB a day. The logs were rotated daily. They were never transmitted anywhere, during normal operation. When things go wrong, sure, we look at them, and generate even more logging. But that was rare. I cannot stress enough how much of a non-issue lo…

> You are very attached to this "voluminous" point. What do you mean by it?

I mean "a lot" or more specifically; "a whole lot."

Here is an exercise which illustrates this. For the purposes here, assume ASCII characters are used for log entries to make the math a bit easier.

Suppose the following:

  Each log statement is 100 characters.
  Each service invocation emits 50 log statements.
  Average transactions per second during high usage is 200tps.
  High usage is on average 2 hours per day.

  100 x 50 x 200 x 60 x 60 x 2 = 7_200_000_000 = 7.2GB / day
> So I ask you to quantify: What counts (to you) as voluminous, as in daily log file sizes, and how many times they are sent over the network?

The quantification is above and regarding log entries being sent over a network - in many production systems, log entries are unconditionally sent to a log aggregator and never stored in a local file system.

Re: The Grug Brained Developer (2022)

#569

Earlier quoted context omitted.

I care about naming, and I find the name of the visitor pattern infuriatingly bad. Very clubbable. I think I have never created one called "Visitor" in my life. Given the syntax tree example from Wikipedia, I think I'd call it AstWalker, AstItem::dispatch(AstWalker) and AstWalker::process(AstItem) instead of Visitor, AstItem::accept(AstVisitor) and AstVisitor::visit(AstItem). "The walker walks the AST, each items sen…

But the idea of the visitor pattern is not, that it itself walks a tree. The idea is, that it doesn't know how to walk the tree and will be passed in elements from the tree, to its visit method. It does only need to know what to do with one element at a time. The walking is implemented elsewhere.

One may pretend that it's not the case, but in practice, the visitor/walker does traverse the tree in a particular, systematic order. Walker implies a somewhat systematic traversal (I think), which is what it does. As the implementor of its visit() methods, it doesn't even matter whether the generic Visitor class chooses the path or the nodes do. Visitor is also super vague. Does it just say "Hi" to the whole tree and then leave? Does it only visit the museums and ignore the parks?

Re: The Grug Brained Developer (2022)

#570
post #465

“Good debugger worth weight in shiny rocks, in fact also more” I’ve spent time at small startups and on “elite” big tech teams, and I’m usually the only one on my team using a debugger. Almost everyone in the real world (at least in web tech) seems to do print statement debugging. I have tried and failed to get others interested in using my workflow. I generally agree that it’s the best way to start understanding a s…

Here is my circular argument against debuggers: if I learn to use a debugger, I will spend much, possibly most, of my time debugging. I'd rather learn how to write useful programs that don't have bugs. Most people believe this is impossible. The trouble of course is that there is always money to be made debugging. There is almost no incentive in industry to truly eliminate bugs and, indeed, I would argue that the inc…

[deleted]
Post reply on HN