Hacker News new | ask | show | jobs
by jerf 2 days ago
Have you been in static types the whole time? It's a really, really common failure state in dynamic programming languages, the Everything Function, that started out as something simple, but then someone added a flag to make it also do this other thing, and then you need a flag to only do that other thing sometimes, and someone needed to operate on multiple things so they made a string parameter also optionally an array, and later someone allowed it to also be an object with this one method, or maybe another method if it's present because some other team implemented that before the first one and can't switch now, and before you know it it's a free-for-all of people adding flags and options and type analysis and if statements and you have a complete mess. Especially if this function is shared by many disparate teams, each of whom isn't "allowed" to break the others, though a single team can fail this way plenty fine.

You can still do this in static languages, but they do push back a bit more because you don't get the flexibility that dynamic languages offer when it comes to accepting a huge variety of different input types.

I've torn a few of these apart over the years. Never fun. Haven't tried with AI but suspect that would only be a quantitative change rather than a qualitative change. The fundamental problem with fixing these is lack of information about the exponential complexity of possible call mechanisms and the AI will have the exact same information problems I will, just faster.

Edit: One of them that I tore apart ended up being two entirely separate functions slammed together into one by historical contingency. I don't just mean that I broke the functionality down into multiple functions, that's a basic tool of how you tear these down and is nothing of note. I mean that one of the "everything functions" I tore down had two distinct calling patterns that were distinct functions that not only shouldn't have been festooned with so many options, but never should have been one function at all because they weren't even conceptually the same thing or even particularly related.

Think of it as two stages of a straight-line process, that were just jammed together because of the fact they got called at similar times, and the original writers weren't clear on the unrelated nature of the tasks and nobody was able to see it through all the obfuscation until I sat down, very deliberately, and I realized this as I was tearing it apart. I don't remember the details, I tend to remember things very conceptually and thus I have a hard time remembering the details of functions with no conceptual purity, but you can get close by thinking of the function as validating incoming parameters, and then applying the parameters to a database. And people were so confused that despite the fact this function, when tickled correctly, could do it all in one shot, sometimes, kinda, with some caveats, there were places where this function was called first to validate (with flags to shut off the application), and then to apply (with flags to shut off the validation). And to be clear, I mean, I did not realize it either even from my contact with the function over the years. It was only when I sat down with it for hours and systematically tore it down that I figured that out.

1 comments

> Have you been in static types the whole time?

In fact, Python has been my most-used language for 15+ years and I rarely use annotations.

> the Everything Function

This doesn't happen in my own code; 10 lines is unusually long for me. In others' code, it comes across that the problem is more to do with not properly splitting up the task (a lot of the time, a reluctance to extract loop bodies, in particular). The case logic does have to go somewhere and it isn't always practical to hide it with polymorphism (which has its own problems; I write my own classes quite a bit less than average I would say, and especially avoid inheritance). It's often better when you have one thing that lays out the case logic explicitly while delegating all the actual work.

But aside from Everything Functions, a lot of code bases have more of a problem with the Everything Class that just contains way too much state and still doesn't neatly refactor the work away (and where there are passing attempts to extract a few lines, they often end up in a "method" that doesn't actually touch `self`).

> I mean that one of the "everything functions" I tore down had two distinct calling patterns that were distinct functions that not only shouldn't have been festooned with so many options, but never should have been one function at all because they weren't even conceptually the same thing or even particularly related.

Yeah, sounds like you work in unusually unpleasant circumstances.

But I don't really see how a lack of type expression leads to this kind of thing. The default assumption for the type of a parameter, in untyped Python, should be: "an object that supports the operations currently used with it in the existing code". Going beyond that is like adding additional methods to a class that hasn't actually been written, and needs to be well considered.

A lot of people on HN don't seem to like dynamic typing. I think it's more that it's not for them, and that's fine. There will always be people with different mental models.