Hacker News new | ask | show | jobs
by hibikir 38 days ago
A non-trivial part of the big difference between the juniors that seem talented and "get it", and those that don't is precisely their ability to form accurate enough world models quickly. You can tell who is going at the "physics" of software and applying them, and who is just writing down recipes, and doesn't try to understand the nature of any of the steps.

It's especially noticeable when teaching functional programming to people trained in OO: Some people's model just breaks, while others quickly see the similarities, and how one can translate from a world of vars to a world of monads with relative ease. The bones of how computation works aren't changing, just how one puts together the pieces.

8 comments

I was even as a junior the kind, who tried to understand the nature of the steps. I failed many times, but I learned from them all the time. I remember my mutable public static variables, and terrible small JavaScript apps. But every time when I did something like that, I tried to understand it. I knew that I failed. Sometimes it took me a year or more (like when I first encountered React about a decade ago, I immediately knew why some of my apps failed with architecture previously).

However, I've seen developers who were in this field for decades, and they still followed just recipes without understanding them.

So, I'm not entirely sure, that the distinction is this clear. But of course, it depends how we define "senior". Senior can be developers who try to understand the underlying reasons and code for a while. But companies seem to disagree.

Btw regarding functional programming. When I first coded in Haskell, I remember that I coded in it like in a standard imperative languages. Funnily, nowadays it's the opposite: when I code in imperative languages, it looks like functional programming. I don't know when my mental model switched. But one for sure, when I refactor something, my first todo is to make the data flow as "functional" as possible, then the real refactoring. It helps a lot to prevent bugs.

What really broke my mind was Prolog. It took me a lot to be able to do anything more than simple Hello World level things, at least compared to Haskell for example.

I had to learn Prolog for a university paper and I have to agree; out of the dozen-ish languages I've had to learn, something just didn't "click" with Prolog.

No real value is this comment, I'm just happy to share a moment over the brain-fuck that is Prolog (ironically Brainfuck made a whole lot more sense).

I wouldn't really try to equate arbitrary job titles awarded based on tenure with actual expertise; titles aren't consistently applied across the industry, or awarded on conditions other than actual merit.
There are a lot of very young developers who have less years of experience than me who have tons more expertise than me.

The problem is, as is evident by this article and thread, it's difficult to measure (and thus communicate) expertise, but it's really easy to measure years of experience.

I vividly remember the moment this clicked for me. I had spent the better part of a decade being interested in programming and essentially learning recipes. It wasn't until I was a couple years into a CS degree and starting to work professionally as a web developer, that I finally had an epiphany of what software actually was, and the degrees of freedom that it actually has. It's very hard to put into words because it was an internal phenomenon, but I can describe at a more visceral understanding of what is meant by "the map is not the territory", and "all models are wrong but some are useful". It's like, you can build anything in software, it's up to you to decide how to do it and make it relevant for a real world use case.

Of course I was still super junior and had so much to learn, but from that point I could at least interrogate any pattern or best practice to understand why it existed and where it should or should not be applied.

I've had conversations with people who wanted to learn how to code. I found that teaching someone how to code is tedious experience. It's just a bunch of memorization and bafflement at how quickly someone else can do things at the keyboard. I've since come to realize that wanting to learn to code is NOT a good starting place. It's best to have a vision. What's the problem you are wanting to solve? If writing software is a way to solve that problem...well NOW we have something to learn around. We have a vision. We have a goal. And learning the syntax and cs concepts is no longer an end of itself, it's just an obstacle to get through to accomplish the vision. You bring enough of these visions to completion, you'll find you've cleared a LOT of obstacles and wow, you've gained a lot of software knowledge.
I've always had excellent model building functionality for abstractions and got the "physics" of a subject rather quickly, be it economics, biology, certain mathematical subjects and more.

Then, I met software and computer science abstractions, they all seemed so arbitrary to me, I often didn't even understand what the recipe was supposed to cook. And though I have gotten better over time (and can now write good solutions in certain domains), to this day I did not develop a "physics" level understanding of software or computer science.

It feels really strange and messes with your sense of intelligence. Wondering if anyone here has a similar experience and was able to resolve it.

I have the opposite experience. Goes to show the difference between people.

I've always had trouble internalizing the "physics" of physics or chemistry, as if it were all super arbitrary and there was no order to it.

Computation and maths on the other hand just click with me. Philosophy as well btw.

I guess I deal better with handling completely abstract information and processes and when they clash with the real world I have a harder time reconciling.

Chemistry in particular is just taught very poorly in USA middle/high school. If anything, it perfectly hinders building that internal understanding.

"Chemical bonds fill the electron shells, which is why we have CO2. But don't worry about why carbon monoxide exists."

"Here's a formula to figure out the angle between atoms in a molecule. But it doesn't apply to H2O, because handwavy reasons. Just memorize this number instead."

Students don't gain an understanding of the subject, because the curriculum doesn't even try to teach it.

This was kind of infuriating about high school chemistry. We were taught so much simply is and that's that. Gold and Mercury differ by one proton, so why is one a dense, yellowish metal and the other one liquid at room temperature? Carbon and Nitrogen sit right next to each other on the periodic table, so why are their chemical properties so different? Why are there so few elements that are ferromagnetic? We dove relatively deep into chemical bonds and isotopes, but glossed over fundamental things like why compounds with similar structures had seemingly random, unrelated properties.
your "physics" grounding is exactly why it feels so odd - software is by its nature anti-physicalist

math and logic are closer to a basis for software abstraction - but they were scary to business people so a "fake language" was invented atop them - you have "objects" that don't actually exist as objects, they are just "type based dispatch/selection mechanism for functions", "classes" that are firstly "producers of things and holders of common implementation" and only secondarily also work to "group together classes of objects"

I feel that is a bit of a false history. OOP was invented by people trying to simulate physical systems, e.g. Stroustup, the Simula people and their contemporaries not business people. Arguably it was popularized later by business people and enterprise Java developers. But that happened way later.

I do not think OOP ever really worked out well as can be evidenced by it no longer being as popular and people having almost entirely abandoned "Cat > Animal > Object" inheritance hierarchies.

This is also a bit of a false history. OOP was squarely invented with Smalltalk. The term was literally conceived for Smalltalk to describe its unique (at the time) programming model. While objects most certainly predate Smalltalk, it was Smalltalk that first started exploring how objects could be oriented.

OOP didn't really take off either, but mostly because it is hard to optimize and impossible to type.

I will have to just disagree with you here. Simula had OOP before Smalltalk existed and both Smalltalk and later C++ arose out of Simula.
fwiw "This paper has described ThingLab, a simulation laboratory."

https://worrydream.com/refs/Borning_1981_-_The_Programming_L...

This happened at an old employer of mine. We started to go down the FP road, veering off the standard OOP of the day. About 25% of the people picked it up immediately. About 50% got it well enough. And 25% just thought it was arcane wizardry.

Between that latter group and the bottom portion of the middle it sparked a big culture war. Eventually leading to leadership declaring that FP was arcane wizardry, and should be eradicated.

>teaching functional programming to people trained in OO: Some people's model just breaks, while others quickly see the similarities, and how one can translate from a world of vars to a world of monads with relative ease.

Besides OO -> Functional this applies everywhere else in Computer Science. If you understood the fundamentals no new framework, language or paradigm can shock you. The similarities are clear once you have a fitting world model.

This resonates. Tips on how to build this skill?
Fail, and try to understand why. Don't be quick with the answer. Sometimes it takes years. But it's crucial to want to improve, and recognize when the answer is in front of you.

Read why programming languages have the structures what they have. Challenge them. They are full with mistakes. One infamous example is the "final" keyword in Java. Or for example, Python's list comprehension. There are better solutions to these. Be annoyed by them, and search for solutions. Read also about why these mistakes were made. Figure out your own version which doesn't have any of the known mistakes and problems.

The same with "principles" or rule of thumbs. Read about the reasons, and break them when the reasons cannot be applied.

And use a ton of programming languages and frameworks. And not just Hello World levels, but really dig deep them for months. Reach their limits, and ask the question, why those limits are there. As you encounter more and more, you will be able to reach those limits quicker and quicker.

One very good language for this, I think, is TypeScript. Compared to most other languages its type inference is magic. Ask why. The good thing of it is that its documentation contains why other languages cannot do the same. Its inference routinely breaks with edge cases, and they are well documented.

Also Effective C++ and Effective Modern C++ were my eye openers more than a decade ago for me. I can recommend them for these purposes. They definitely helped me to loose my "junior" flavor. They explain quite well the reasons as far as I remember.

I am curious about that nit on list comprehensions in Python: what do you mean, why are they a "mistake" of language design?
So when they designed it, it wasn’t that bad for simple cases. However, with more complex nested lists, there isn’t a clear data flow, it jumps from one place to another. Especially the first term is problematic. It’s not beneficial at all for the modern IDE based development. So at the end, this is a better list comprehension in this sense:

`[state_dict.values() for mat to mat2 for row for p to p/2]`

Or similar, where data flow is 1->2->f(2)->3->4->f(4). Where right now it is this lovely mess with one more repeating term:

`[p / 2 for mat in state_dict.values() for row in (mat 2) for p in row]`

Where the flow is f(4)->2->1->3->f(2)->4->3

This is not just a Python list comprehension problem obviously. The simple for… in… has a similar problem. It’s only better, because the first term `p/2` is at the end.

I'm struggling to even understand what you have in mind, because HN doesn't do Markdown formatting and asterisks are interpreted for emphasis across lines. But I've never really thought there was a problem with the syntax. To me it reads naturally, left to right: "A list ([) of the results from calculating whatever, (for) each of the (name) values that are (in) the (names) container". With multiple clauses, they're in the same order as the corresponding imperative code, which also makes sense. (Perhaps if "for" were spelled "where", it might not...)
You seem to be complaining more about for working on iterators/generators like range() and not on comprehensions themselves.

List comprehensions are inverted (syntax-wise) compared to regular program flow, but that is pretty easy to learn and adapt to (and is, imo, much better than "a = b if x else c").

With my solution, you don't need to adapt... that's the whole point. There is no inversion.
Put yourself in a position where it is your problem/responsibility, where you cannot depend on another to do it for you. You'll be learning every day.
If your hair is on fire, you don't ask how hot.
No who you replied to, but practice. Deliberate practice; not just writing the same apps over and over, but instead challenging yourself with new projects. Build things from scratch, from documentation or standards alone. Force yourself to understand all the little details for one specific problem.
Indeed. Understand the principles, you can work with just about any tool
there's actually a really good book that bridged it well for me when I was doing my bachelors, A Little Java, A Few Patterns. this is from the famous lisp books for groking FP.