Ask HN: Concepts that clicked only years after you first encountered them?

579 points by luuuzeta ↗ HN
I'm reading Petzold's Code [1], and it dawned on me that I didn't understand logic gates intuitively until now. I took a Computer Architecture course back in college, and I understood what logic gates meant in boolean algebra but not empirically. Petzold clarified this for me by going from the empirical to the theoretical using a lightbulb, a battery, wires, and relays (which he introduces when he talks about the telegraph as a way to amplify a signal).

Another concept is the relationship between current, voltage, and resistance. For example, I always failed to understand why longer wires mean more resistance while thicker wires mean less resistance.

[1]: https://www.codehiddenlanguage.com/

945 comments

[ 2.7 ms ] story [ 547 ms ] thread
Unit testing and using dependency injection to write test-able code.

I'm not sure if it was years, but it wasn't immediate. I just didn't understand why dependency injection was good at first, and not just someone's weird personal code style choice.

I thought it was just people being "Enterprisey" which I'd encountered many times over the years.

Once I committed to unit testing, I realized how necessary it is.

Unfortunately I still encounter customers who haven't bought into it and as a result have untestable code. It's so hard to go back and retrofit testing.

> dependency injection

The term is unfamiliar to me -- is it related to "fault injection"?

No it is a design pattern to structure code. It is also known as the hollywood principle (don't call us, we call you). Meaning that dependencies of a class are provided from the outside, the class itself doesn't know how to create instances of dependencies, just relys on the dependency container (also named inversion of control)...
It is a pattern where you inject the dependencies of a class into it rather than create the instances of the dependencies within the class. for more info --> https://en.wikipedia.org/wiki/Dependency_injection

it makes the code cleaner and testable.

What I find troubling about that page is that it does not qualify up front whether the dependencies are:

A. Also depended upon by other, coequal unrelated classes, possibly maintained by others, OR

B. Depended upon only by a single class (of higher functionality)

Situation A is sometimes called a portability interface; B might be called an internal structuring interface.

The difference has crucial implications for social power relationships between the human developers and users involved.

Let's say Class A depends on Class B. A lot of people have the instincts to have A construct B so that the callers of A don't have to worry about it.

But this makes testing A in isolation difficult. When testing A, you want to mock out B with an instance the test can manipulate.

So we want A to not create B, instead we want B to be "injected" into A. The general strategy of having B passed into A is called dependcy injection.

The idea of DI is that you creat resources indirectly, and don't call the named constructor in your code. You then use interface types to interact with the resource.

This way, your code isn't bound to a specific implementation.

Some DI frameworks even go so far and define all resources in a config file. This way you can switch out the implementation without a recompilation.

(comment deleted)
Going to steal a description I wrote for a blogpost several years ago, when I had recently understood DI for the first time so it was very fresh in my mind:

https://hasura.io/blog/build-fullstack-apps-nestjs-hasura-gr...

Dependency Injection solves the problem of when you want to create something, but THAT something also needs OTHER somethings, and so on.

In this example, think about a car.

A car might have many separate parts it needs:

  class Car {
    constructor(wheels: Wheels) {}
  }

  class Wheels {
    constructor(tires: Tires) {}
  }

  class Tires {
    constructor(rims: Rims, treads: Treads)
  }

  class Rims {
    constructor() {}
  }

  class Treads {
    constructor() {}
  }
We can manually construct a car, like:

  const car = new Car(new Wheels(new Tires(new Rims(), new Treads())))
But this is tedious and fragile, and it makes it hard to be modular.

With dependency injection, it allows you to register a sort of "automatic" system for constructing an instance of "new Foo()", that continues down the chain and fetches each piece.

  class NeedsACar {
    constructor(@Inject private car: Car) {}
  }
And then "class Car" would have an "@Inject" in it's "constructor", and so on down the chain.

When you write tests, you can swap out which instance of the "@Injected" class is provided (the "Dependency") much easier.

I know the car is a classic, cliche OOP example, but for any semi-experienced programmer, I feel like it's a really bad choice of analogy which only serves to obscure how you would use a technique like DI in actual real-world code, rather than an artificial toy example.
I think you might have it backwards. The first example _is_ dependency injection, too, since you are passing the required instances into the constructor. The non-DI approach would be for the Car class to import and instantiate the Wheels class inside its constructor function.
The first example is also dependency injection. The rest of what you described is why a dependency injection framework often becomes useful in a system with a large dependency tree.

The alternative to dependency injection is for functions to instantiate dependencies internally rather than having them be passed in from the calling context.

I'd probably reach for a Builder pattern first for that rather than go full DI container.
People would have way fewer opinions on it if it was just called "passing stuff in"
I think the poster is referring to Guice or something similar - that there's a framework which selects which instantiated runtime object gets injected as a dependent into another, thereby automatically "figuring out" what object needs to get instantiated first, and then next etc.
You'd do fault injection by injecting dependencies that respond with / throw faults.
Dependency injection is one of those technical terms that are made up to describe something that has been done for decades but with a new name because the creators of the new term don't have much experience and/or they want to gain notoriety for creating a new programming technique.

If you've ever written a constructor for a class that has arguments (within the constructor signature) that are used by the instance of the class when instantiated then you have done dependency injection, or put more simply 'passing stuff in' which was eloquently stated in another comment on this thread.

I believe the term Dependency Injection was coined by Martin Fowler, as the meaning of the pattern name Inversion of Control is less obvious. I'm going to assume that Martin Fowler has quite a bit of experience, although you might accuse him of wanting to gain notoriety...

In any case, DI is not at all a new trendy term. Inversion of Control dates back to the gang of 4 patterns book from the '90s. Yes, constructor parameters are one way to implement DI, but not the only one. I'm ambivalent on the whole pattern movement, but they are a great educational tool for novice programmers, and it is good to have some standard terminology for core patterns.

To put it another way, constructor based DI is not simply the use of constructor parameters, it is understanding the OO design principle that side-effecting code should be isolated into objects, rather than just dumping a bunch of DB queries into your functions, like we did back in the stone age.

Can you explain the concept? I feel I didn't grok it fully
Not sure about poster above, but I found a large amount of value in writing tests when developing API backends. I knew the shapes and potential data. Was easier to write tests to confirm endpoints looked right than manually hit the api endpoints.
Always amuses me when I see tests spin up a http server, just to call a function.
What specifically amuses you?

Todays http servers may have any number of request altering/enhancing "middleware" calls between the incoming request and the actual business logic/function.

How do you ensure that your api works as designed if you only test (pure) business functions? Or do you re-create the middleware chain of functions manually for you test?

technically this would be considered an integration test I think.
/api/foo -> fooApi()

You don't need to test the call to /api/foo, you only need to test the call to fooApi(). It doesn't/shouldn't require a http server to do that. Just call the function directly.

If you want to test that /api/foo exists, that is essentially a different test and only requires a mock version of fooApi(), because you've already tested fooApi() separately above.

The benefit of this approach is that your tests run a lot faster (and easier) not having to spin up an entire http server, just to test the function.

As for the middleware... that is also tested separately from the business logic. You don't want to tie your business logic to the middleware, now do you? That creates a whole dependency chain that is even harder to test.

Obviously, you are doing it correctly and not putting all your logic in the controller.

See also: Frontend development that requires running the entire stack in order to test it.

The controller in this example is fooApi(). I generally reserve this layer for taking the input parameters (ie: query/post data), and then passing that data to some sort of service which executes the business logic on that data.

For example, the business logic is what talks to the database. This way, I can test the controller separately from the business logic. Often, I don't even bother testing the controller, since it is usually just a single line call to the business logic.

Anyone who writes a lot of tests realizes very quickly that in order to write testable code, you have to separate out the layers and compartmentalize code effectively in order to allow it to be easily tested. Tracking dependencies is critical to good testing (which is where this whole DI conversation got started).

If you aren't writing code like this, then you're just making testing harder on yourself... and then we end up with tons of code which can never be modified because the dependencies between layers are all too complicated and intertwined. Don't do that.

At this point in my 27+ year career, I don't see any reason to not do things correctly. The patterns are all instinctual and no need to try inventing something new, I don't even think about it any more, I just do it from the start.

Sorry, I probably wasn't clear. I wasn't spinning up the server itself, just testing the endpoints via their functions. Though this likely falls more under integration vs unit tests.

As for unit tests... I mostly add them to projects with something egregious happens or a very hard bug to spot can occur - just to prevent anyone else from foot gunning themselves (here be dragons or whatever).

You were clear. I was agreeing with you.
It used to be hard. I just spent a day in c# and gql figuring out how to do API level tests with a new framework. But when you get it working it's ever so much faster.

https://github.com/gaffo/CSharpHotChocolateZeroQLIntegration...

Still playing around with the right level for this but this is currently nice as it gives me compiled type checking and refactoring. This is an example/extraction from another project which uses react as the client. Wasn't big on cross language api level tests yet for speed of development, as that's a tradeoff. Redundancy vs even more framework.

Seven years into my career I'm increasingly convinced that the emperor has no clothes with respect to unit tests that are just transcripts of the code under test with "mock.EXPECT()" prepended to everything - 95% by volume of the unit test code I've ever read, written, or maintained.
Yeah, the mere presence of unit tests is not enough. It has to actually assert something useful.

When I code review, I try to make sure I call out "fake tests".

The useful assertion to be made about gateway/repository layer code is that it gets the expected behavior out of the dependency. This is not an assertion you can make when you've mocked out the dependency. You must make it in an integration test, not a unit test. Unit tests in these layers just make assertions about "it calls this method on the client" or "it sends this string," which tells you nothing about whether doing that is actually correct.

It's relatively uncommon for handler/controller code to have logic worth testing, most of the time it's just maintaining the separation of layers and concerns by wrapping gateway/repository calls. All there is to assert about it is that "it calls this function in the next layer."

Every once in a while there's nontrivial functionality to test in the middle, and unit tests can often be a good fit for that, but in my experience it's more the exception than the rule.

That’s a great point. Seeing lots of mocks and assertions that certain functions are called is often much ado about nothing since no actual code functionality is exercised. I do sometimes see the return value functionality of mocks used as a stub, just because the dev hasn’t internalized the distinction and can “make it work” with a mocking library.

One of the only legit use cases for mocks that I have personally come across is validating things like a sequence of API calls to an external service, or queries to a database where there is a check that certain efficiencies are guaranteed, e.g. verify that an N+1 select problem doesn’t creep in, or knowing that a app-level caching layer will prevent redundant API calls.

Yep, I think mocks are mostly a smell. The "functional core" of a module should be entirely or almost entirely unit testable with (possibly fake) dependencies passed in. The glue code ("imperative shell") should be tested at a higher level - "integration" or "end to end" or whatever you want to call it - which looks at the externally observable effects of running the code (database changes or API responses or whatever) rather than the details of its execution.
(comment deleted)
This is the way, but it’s tough to sell “we’re not going to unit test this part” in a professional setting where there are incentives to look more responsible than thou, directors are looking at unit test coverage reports, etc.
Yeah I have no qualms about writing mostly-BS unit tests when the existing structure of a project makes it infeasible to write good ones. When in Rome! But when I'm starting from scratch or have a chance to do refactoring, I move toward the "functional core" approach as much as possible.
You can still get code coverage points with broader tests.

I think it’s reasonable to disavow expecting single unit tests for nearly every method on every class with mock/boilerplate/copy-paste hell. However, I would still expect “local integration” unit tests that exercise broader chunks of code and keep code coverage up.

So it should be “I don’t need to write a new unit test for this because an existing unit test calls the method that calls this method without it being mocked and you can see it’s covered in the code coverage.”

Code coverage in Go is on a package level. A test that exercises a handler, controller, and repository can accrue coverage for at most one of them.
Huh. I guess I’ll add that to the list of reasons I don’t like Go.
How are mocks a smell if "unit testable with (possibly fake) dependencies" is okay? That's what mocks are.

Or do you mean specifically "expecting" interactions with those mocks? Because I agree that's usually not that valuable.

Fakes are not mocks. A fake is an actual implementation of the dependency that runs in-process/in-memory. This means that, unlike with mocks, your test code does not dictate the behavior/outputs of the dependency.

Ideally, service/library owners would write and maintain the fake to ensure that it stays in sync as changes are made to the actual service.

Basically what Cyph0n said, fakes and mocks aren't the same thing.

But it isn't quite as smelly when I see a mock (or "stub" more typically) used as an expedient way to create a fake to pass in as a dependency.

Like you said, it is asserting on the interactions with a mock that is what primarily smells bad to me. I very rarely (honestly I think maybe never) see this done in a way that isn't just rewriting the implementation of the method as expectations in the test.

Amen. I have been frustrated at many jobs where developers were so proud of endlessly writing mocks and spending more time on their tests than on core functionality.

Over time I have favored very basic unit tests and leaning in more on automated integration and regression tests. I know the theory of unit tests catching things earlier, I just don’t think it matters in practice.

I think in practice unit tests are both the most and least useful tests. Unit tests of code that just generates output as a function of input are the most useful, and unit tests of code that just glues together a bunch of side effects are the least useful. I like to find ways to structure things so that it is mostly comprised of the first kind of code, and has so little logic in the second kind of code that it's easy to justify not unit testing it.
I once saw type checking presented as kind of an alternative to unit tests which doesn't rely on the assumption that developers write good tests, and I really like that viewpoint. A powerful type system can prevent many bugs, doesn't need additional test code and doesn't make any assumptions.

Of course it's not necessarily a full replacement, but it's definitely better and more time efficient than bad tests.

Yes, static analysis of types can eliminate a big class of unit tests (basically just checking pre- and post- conditions on types), which is a big part of why it's great. But most unit tests should be for logic. But that's why they aren't very useful for "glue" methods which just thread things through different dependencies - they shouldn't have much interesting logic.
> Of course it's not necessarily a full replacement

I'd actually say: Not in the slightest. A type system just rules out illegal input values[0] but it won't ensure that your business logic is correct.

And even that[0] is not entirely correct because, most of the time, a type system just rules out some illegal values but not all of them, because it cannot represent all possible constraints. Gary Bernhardt discussed this whole type checking vs. tests debate at length here: https://www.destroyallsoftware.com/talks/ideology

I call those lockdown tests and they're a smell. You don't want to dictate the implementation, just the inputs and outputs. The former leads to very brittle code, the latter describes and validates the contract. It's also important where you put the logic of the test in the code base when you have multiple layers. This latter part is harder and system dependent.

In many cases mocks are now over used where previously they were important in say 2008. Especially now with languages that support functions as objects, better generics, and other features which weren't common a while back. Likewise frameworks are and languages are generally way more testable now which means you're doing less backflips like static injecting wrappers for DateTime.now into libraries to make tests work. This further allows more contract and less implementation specific testing.

As with most things there is a lot of nuance/art to doing it well and smoothly

> You don't want to dictate the implementation, just the inputs and outputs

When programmers really embrace dependency injection like the parent comment, the implementation largely is the inputs, and your code expects them all to be implemented correctly (or mocked correctly by test code). I completely agree that mocks are overused, and their importance in 2008 were in my view a direct result of the popularity of dependency injection patterns.

Following that logic, the best way to remove mocks from your tests is to minimize dependency injection, relegating it all to a single place in the code wherever possible, and implementing all other domain logic as pure functions of input to output. How to test that code which touches other systems? Integration tests (or multi-system tests or whatever you like to call them).

I generally agree with you, DI went really far in many ways through up to like 2015.

In 2008, I actually found code often got more testatable when it was dependency injected. I spent a month ripping singletons out of a gui application so we could make it into a web service, that would have been a lot easier with a DI model. The system was nigh-on untestable until we got rid of those singletons.

I link it in another comment but https://github.com/gaffo/CSharpHotChocolateZeroQLIntegration... is how I do things these days, api integration test, and more focused unit tests where you don't understand the library well yet, there's lots of tricky edge cases, or error conditions such as setup that are harder to integration test. I'm still feeling it out.

I think my favorite of these was a function that called 4 other functions and didn't do anything else, its unit test mocked out all 4 other functions and asserted that each was called.
Every damn day. It’s the thing that puts me most at risk of falling out of love with software engineering.
I had a take home test a year or two ago and I got grilled during the interview for not writing crap like that.
How would you unit test that?
This was in django, where a good such test would set up the database, call the function, then check that the new state in the database is as expected.

(Which IIRC is what I changed it to)

I've seen tests like that ("has the mock been called")... and in general I've felt that they were kinda useless.

Thinking about the original TDD approach -- red, green, repeat maybe it's not that bad? I prefer your approach, where you actually verify the state change. But in the absence of that, I wouldn't mind the "call the mock" approach. At least it does change some of the contract?

I would say it's worse than no test, because it discourages refactoring. Any alterations would make the test fail, even if it still does what it's supposed to do.
It’s better than no test because it makes the metric go up, and as we all know, virtuous and rational engineering practice is about recognizing that metrics are the objective truth about The Good and anything else is just your opinion. /s
I've got mixed feelings about this. I'm leaning towards agreeing with you, because testing for the most part should be about the contract and not specific implementation. However in rare cases, as part of early development phase perhaps, I could see this being a useful signal. Provided it gets refactored later on.

I suppose having lots of mocks is a smell after all..

Another solution is to eliminate classes and only use structs or similar plain objects. Makes mocking and testing functions much easier. At this point I see no reason for OOP whatsoever and consider it a big mistake.
I agree with OOP not being needed at all, but I don't agree on this being an alternative to dependency injection.

However you structure it, there will always be "glue code" that ties the nice inmutable code with the outside-interacting bits, and if you want to unit test those, dependency injection (with functions or state, not classes or instances) is still the way to go.

True, but there might not be a big need for unit testing the glue code. By its very name it doesn't contain much logic to test. Integration tests are more valuable in this case.
This seems totally orthogonal to me.

Using a totally non-OOP functional style, you can either instantiate state within a function or pass it in from the calling context, which is the same trade-off that dependency injection targets.

If you work on a legacy code base with tens of many millions of lines of code, the non-OOP will be a better code base. My opinion.
I wasn't disagreeing (or agreeing) with that opinion. It's just orthogonal to whether or not dependency injection is a useful approach.
Getting rid of data abstraction and encapsulation is throwing the baby with the bath water.

The abstract concept of OOP (messages between complex objects, as defined by Alan Kay) is an attempt at mimicking biological systems. Most modern languages implement data abstraction, but call it OOP, where they encapsulate some functionality with the data it operates on. Really helped with varying data formats in the AirForce in the 60s, apparently. There isn't anything wrong with this abstract concept either - it's a way of structuring a solution, with trade-offs.

Support for unit testing and mocking has little to do with OOP, and everything to do with the underling platform. Both C++ and Java, for example, do not have a special runtime mode where arbitrary replacement of code or data could occur. This is necessary for mocking functionality that is considered implementation detail and hidden by design. The hidden part is great for production code, not great for testing.

For example, if an object in java has a field like 'private final HttpClient client = new CurlBasedHttpClient();' this code is essentially untestable because there is no way in Java to tell the JVM "during testing, when this class instantiates an HttpClient, use my MockHttpClient".

Kotlin fixed some of that with MockK, which can mock the constructor of a Kotlin object, and you can return your mock implementation when the constructor is invoked.

Clearly, it's a platform issue. There could be a world where you could replace any object in the stdlib or any method or field with a mock version. JavaScript is much more flexible in that regard, which is why unit testing js code is much easier.

The root of it all stems from the fact that unit tests need to change some implementation details of the world around the object, but production code should not be able to, in order to get all the benefits of encapsulation.

If you get rid of modern OOP, you are swinging the pendulum in the opposite direction, where your tests are easy to write on any platform, because everything is open and easily accessible, but your code will suffer from issues that creep up when structures are open and easily accessible, such as increased coupling and reduced cohesion.

I believe that combining state and functionality - the root of OOP - is a mistake. Tons of programming patterns and concepts exist to solve this fundamental mistake. When you stop using classes all of your code becomes so much cleaner, easier to reason about, test, debug, and so on. You can never create only pure functions in the real world but you get closer to this ideal.

I stand by my statement that OOP is totally unnecessary.

You can believe that the world is flat, announce that boldly and stand by your statement. It doesn't mean it's true in reality, only that it's true in your mind.

Edit: This is more blunt than I intended it to be. For what it's worth, I happen to agree with you in principle, but I also think you're taking the roof off the car here and boldly claiming that the experience is so much better in the summer. What about winter? What about when it rains? What are the trade-offs? Not mentioning the downsides means that you either haven't found them, haven't thought about them or are intentionally omitting them from the discussion.

No need to tone it down; You were right in calling out the GP.

To dismiss the whole of OOD/OOP so cavalierly just goes to show they don't know what they are talking about.

Much of the success of Modern Software is directly due to the wholesale adoption of OOD/OOP in the last few decades.

OOP is successful despite of itself. My point is it’s just an inferior way of programming and there is no reason to use it other than legions of OOP programmers who take it as gospel. Obviously this is an opinion but it’s informed by lots of experience in new and legacy code bases.
To be blunt, you are stating "opinions" without any basis in facts; hence it is hard to take you seriously.

Separation of Concerns, Modularization, Reusability, Type Hierarchies, Type Composition, Interface contract-based programming, Frameworks etc. were all made mainstream by OOD/OOP. These are things taken for granted by programmers today. As somebody who has been doing OOD/OOP since the early nineties i can tell you it was the single biggest reason for the explosion of Software in the past few decades.

As a concrete example, early in my career i had programmed in C using the Windows API; both 16-bit and Win32 (Thank you Charles Petzold). It was difficult, tedious and a lot of work. And then Microsoft introduced MFC (Microsoft Foundation Classes) Framework with Visual C++ IDE. With a few clicks of the wizard, i had a complete skeleton application with a lot of hard work already done for you. That was a revelation for me on the power of OOD/OOP. Things i had slaved over in Win32 was now at the fingertips of every noob who could type. The same revelation happened (but not to the same extent) when i moved from Xlib to Motif on Unix platforms.

I had pointed you to Bertrand Meyer's book OOSC2 (in my other comment) as the book to read to understand OOD/OOP. Another great book to study is Barbara Liskov and John Guttag's Program Development in Java: Abstraction, Specification, and Object-Oriented Design.

The major reasons software exploded are the internet and the ubiquity of computing devices that get cheaper, faster, and smaller. Language had nothing to do with it, with or without OOP.

I was formally trained and cut my teeth in OOP code bases and my latest company we are very light on classes. 99% is plain objects and modules that operate on them. Everything is very straightforward and logical. In my side projects I’ve stopped using classes as well and it’s so much cleaner.

This is opinion and preference. If I hired a guy who wanted to litter the code base with AbstractUserBeanFactoryFactories I’d have to let them go because it’s a situation I don’t want anymore.

>The major reasons software exploded are the internet and the ubiquity of computing devices that get cheaper, faster, and smaller. Language had nothing to do with it, with or without OOP.

This is putting the Cart before the Horse. Computers, Internet etc. can't do anything without the Software to drive them. That software is written in some Language/Tool using some Paradigms and Software Engineering principles. The ease of use, ease of structuring, ease of understanding etc. of these are what drives Creation, Adoption and Expansion of Computing and Devices.

>I was formally trained and cut my teeth in OOP code bases and my latest company we are very light on classes. 99% is plain objects and modules that operate on them. Everything is very straightforward and logical. In my side projects I’ve stopped using classes as well and it’s so much cleaner.

If you think merely using a syntactical structure like "class" (and Design Patterns) is what makes code OOP, then you don't understand OOP. You can do various degrees of OOP without language syntactical support. This is why i listed the principles and not some syntactic sugar in my previous comment. It is a way of thinking and Software Engineering which has given the most "bang-for-buck" so far in the Industry.

>This is opinion and preference. If I hired a guy who wanted to litter the code base with AbstractUserBeanFactoryFactories I’d have to let them go because it’s a situation I don’t want anymore.

Opinion and Preferences must be based on facts else it is only as good as "Flat Earther" category and nothing more can be discussed.

I don't understand but am intrigued. Can you point to some resources that elaborate on and demonstrate what you're talking about here?
Yes but if you represent 2 functions over some state a(s) and b(s) then you have the same issue with this as if it was:

Class s: fun a(self)... fun b(self)...

The problem is mutability not how state is bound to a function.

The idea is to make the functions pure and return the state ‘a(s_1) -> s_2, value’. Makes so much easier to test and write concurrent programs.

Though it’s not always practical, like when needing to push an element to an array for making updated state. It is nevertheless possible to shave of pure parts considerably making mutability easier to maintain.

Recursion is an obfuscated way to run a loop with a stack. At first it seems like magic, and makes things so much easier. When trying to replicate it the first time actually using a loop and stack it’s really hard to get right. But by the time you’ve done it two or three times, it’s as easy as breathing.

Similar, OOP is an obfuscated way to run a function with a context. The first time you separate the data in the context from an object, it’ll be hard to get it right and make it easier than to just use Objects and Methods. But once you’ve done it two or three times, it’s as easy as breathing.

You can optimize loading the context to be better than copying a bunch of unaligned data in lots of ways, but polymorphism is a common way to get there.

OOP does not have a monopoly on abstraction and encapsulation. What OOP does, namely object-level encapsulation, is just a very extreme way of structuring code around mutable state. IMO the better alternative is to avoid mutable state as much as possible and keep data and code separate. Code structured as pure functions is easy to test and encapsulation can be done at the module level.
My point is that improving the state of unit testing has little to do with OOP, and everything to do with the environment that the code runs in.
OOP does not require mutable state. OOP has its origins in imperative languages, and so in the past OOP often involved poor management of mutable state.

I am a Scala programmer, and our code mixes OO and FP. Almost all of our classes are completely immutable.

In the end, you will always need to encapsulate data and functions in some manner. The FP approach involves module systems, but as I understand it, objects in Scala actually provide a better module system than found in pure FP languages.

(comment deleted)
OOP tends to encourage thinking in more abstract ways than is often necessary imho. I feel like learning a language with only structs had a positive effect on my coding ability (as someone who more or less started with Java).

The few times I had to use Java afterwards I felt the same - all OOP features were unnecessary or at least didn't feel like the most straightforward approach. Nowadays I never use classes in Python, JS etc., it's just not needed - and in the case of Python it makes JSON insanely cumbersome.

I‘ll add to that: the difference between "offline" unit testing and spring integrarion testing with test containers and real application contexts + all the related concepts like @SpyBean, @Mock, @MockBean...

I always hated testing and I still do, but every time I commit to doing it right I catch so many errors before QA.

+1 to this. It doesn't help that some dependency injection frameworks' (ahem, looking at you Dagger2) error messages can be convoluted and hard to understand.
I keep looking for a reason to write software using a proper DI container framework. In talking to Java devs most of them just mention injecting mocks for your database. You don't really need a full DI container for that you could use a service locator instead or some even lighter DI pattern. If you have that particular hammer though and know how to use it, then it becomes easy to achieve and use config to inject your backend.

But that's a long way from glorious full DI containers where you never call 'new' in your code anywhere and all object creation can be dictated by config. I suspect that must be only the kind of thing that people who maintain 1,000,000 line codebases that are at the center of massive bureaucracies.

At my bigco, there's no service-level DI (c++)

Just vanilla C++ classes, and virtual interfaces if we need to mock things for unit tests.

No automatic wiring of the hierarchy.

I'm thinking more like programmers working at the IRS or SSI or something like that, where you have million line long decades old codebases. Not anything that Google or Amazon would write. Applying a DI framework right from the start may actually be a best practice--that allows the codebase to evolve under the next 10 years of contract programmers--instead of just being the YAGNI that it would to anyone in the tech sector.

Although you might say that microservices are like a distributed DI framework that would let you rewrite or mock components provided that the rewrite/mock adheres to the API framework (curiously, I've actually seen that used in tests with a quick and dirty in-memory sequential unauthenticated server used for testing clients where the server passed the same API test suite as the real server).

Agreed, this has taken me years. It also is tough because lots of people over use it IMHO. When the logic keeps bouncing around dozens of classes its too hard to follow, even if its easy to test.
The thing that made it really click with me was the quote (two whom I'm not sure it belongs to) that is roughly "code to abstractions/interfaces, not implementation".

The idea that I take advantage of good abstractions and I send those objects into my classes that need to perform actions via those abstractions made a lot of sense. Helps enable good polymorphism, as well as unit testing and other things.

I don't think I'm doing it justice, but the idea took a good while to understand there reasons behind it. Some books that helped me grok the idea were

- Patterns of Enterprise Application Architecture - Clean Architecture - Architecture Patterns with Python

along with running into problems that could be easily solved with a decent abstraction at work and learning to apply it directly.

Basic music theory.

Almost no one I encountered bothered to actually explain anything. They simply regurgitated things and I guess expected me to somehow intuitively understand something or other.

Sadly, mathematics classes are like that as well. Instructors start throwing equations on the board, expecting us to somehow connect it all together. The best math textbook (Theory of Algebra) I ever read had little sections about the person who revealed a particular subject, why they were studying it, and how the subject is used.
Do you remember what the book was called, or the writers.
Sadly many professors fall victim to the Curse of Knowledge. It doesn't help they need to follow a tight schedule and intuition isn't something you can develop in a single lecture. I suppose self-study and repetition is the most likely solution.

>The best math textbook (Theory of Algebra) I ever read had little sections about the person who revealed a particular subject, why they were studying it, and how the subject is used.

I've found the best type of books provide motivation for concepts, how they have evolved, etc.

Take Computer Science for example, many of its concepts were area of research for decades but from a student's perspective it seems these concepts were always here instead of being constantly refined until the states they're now in.

Exactly, understanding the intentions and history behind a concept is key to achieving comprehension. I've had to teach myself nearly all of the more advanced mathematical concepts I know, and I'm finally starting to reach a point where I feel I have a nice approach toward achieving an understanding:

- Learn the definition - Learn the motivations and history - Peruse a few examples - Try to map the above to a brief synopsis that explains the concept in intuitive terms that relate to your own life. Rely on pictures.

Finally, I find it helpful sometimes to try and "deduce" identities from "first principles". e.g. assume I didn't know that n^0 = 1. How might I reach that conclusion? If I have an understanding of what exponentiation means I should be able to come up with a few different propositions (these could even be relatively informal) that make such a conclusion make sense.

My childhood was rife with mathematics teachers that focused more on rote memorization of identities instead of careful explanation of definitions and development of "intuition". There's pretty much no better way to ensure you'll produce students that dislike and suck at math for the rest of their lives than proceeding by mind-numbing rote memorization.

I think that's a great approach. If possible, you can add another step: Teaching it to someone else.

>There's pretty much no better way to ensure you'll produce students that dislike and suck at math for the rest of their lives than proceeding by mind-numbing rote memorization.

I couldn't agree more.

I recently learned what solfege actually is from a simple ChatGPT conversation after hearing about it for many years.
Any good resources that do it the right way?
I've been using Punkademic and I'm very happy with it.
Looks like I have to pay before I can see what "Tools you will need to learn Music Theory quickly and efficiently".

https://www.punkademic.com/course/music-theory-comprehensive...

That's how a paid course works?
Yeah, but I'm not going to start a course if I don't know what tools and equipment I need for it, upfront.
They have a free trial. And a YouTube. And the courses are republished on tons of platforms, each of which has some sort of preview.
Michael New on Youtube.
This site is nice https://www.musictheory.net/lessons

Music theory is not inherently complex, but the notation adds incidental complexity -- it's sort of like the QWERTY keyboard; once you're stuck on a suboptimal way of doing things it's hard to move off.

Oh my god. Music and photography are my two pet peeves. They have their own twisted vocabulary for everything that surely exists for historical reasons, but might as well be purpose-built to obstruct polymaths from connecting their concepts to other concepts in other fields.

My single largest goal when I'm teaching, is to find an appropriate analogy to a concept that my student already grasps, and bridge it to the new concept. Some fields make this super easy -- MechE and electronics, for instance, if you understand one, you're well on your way to understanding the other -- and some make it super hard.

While I agree with your goal of finding analogies when teaching new concepts, I think your first paragraph is a pretty unsympathetic take on music theory. (I don't know anything about photography.)

Music theory is descriptive, not prescriptive. Is it any surprise that something we had to invent a whole new symbolic notation for is difficult to connect to concepts we can describe in natural language?

If you want to go really deep into the philosophy of music, to understand it from "first principles" so to speak, I highly recommend Leonard Bernstein's lecture series "The Unanswered Question" - https://en.wikipedia.org/wiki/The_Unanswered_Question_(lectu... (available on YouTube).

To paraphrase Bernstein's comment from his book "The Joy of Music": we need to stop comparing Beethoven to grassy fields and mountain streams. A "major seventh" or a "plagal cadance" are themselves the description of those qualities. By all means use music as metaphor, but when you're trying to describe it you're going to be using terms specific to its domain.

I can't find the link. But I saw a minidocumentary about a person who trained his ears to listen to harsh noises. Once he attuned his ears, he could write music in those new musical notes.
Systems in equilibrium. A lot of my college engineering courses had these (what seemed to me to be) hand-wavy assertions of equality and what seemed like just an assumption that the system would converge to that point.

I was probably in my late 30s or early 40s before I really grokked why that tended to be true. (I could blindly accept and grind through the equations to get the answers in college, but it was decades later that I developed a feel for why.)

Discrete mathematics and all sorts of its application in real-world (software development) related problems. Also how any given solution to a problem in one problem domain can be transferred to a problem in another unrelated domain. Think Galois theory but waaaay less fancy :-)
When I did Mathematics A-Level's, we had to select four modules. I chose pure 1, pure 2, stats 1 and, new that year, discrete 1.

So many useful groundings in various super important comp sci concepts.

That professors were thinking real hard about which problem sets to give us in hopes that we would actually learn something.

I don't think I really understood that completely until I started TA-ing.

Tangentially related but that's something I've come to realize too as of recent. Asynchronous programming in JavaScript is new to me so as an exercise, I'm writing a document where I explain the concepts to myself and once I shifted from "how I understand" to "how I would this to someone else", figuring out a good enough scenario/problem harder.

Good problem sets that aid studets' intuition aren't easy to come by, oftentimes they're either too easy or too hard.

The autonomic nervous system and the adrenal cortex. Homeostasis is taught as a textbook fact, the body reverting to a baseline over time. What’s not taught is how much the impacts of daily life events drive a continuous stress response. Fight or flight is not just a reaction to deadly threats. It’s active every moment of every day to ensure survival. The adrenal cortex is always active, to traffic, your boss and colleagues, relationship struggles, and overall health and wellness like sleep and nutrition. Yes, the system reverts to a baseline over time but how much that baseline varies is obvious in tracking resting heart rate.
Any books you could recommend?

I presume you already know of this book: The Body Keeps the Score: Brain, Mind, and Body in the Healing of Trauma by Bessel van der Kolk.

That’s a great one. I’ve been looking for something that links the physiology with knowledge work and life demands, but I haven’t found it yet.
I'll add one more book reccomendation on this subject: Sapolsky's "Why zebras don't get ulcers". It's a really eye opening summary of the physiology of the stress response literature. Quite scary at times.
Blue Noise dithering based on a HN post from @todsacerdoti. https://news.ycombinator.com/item?id=25633483 At my previous job we had an ASIC hardware block to implement blue noise dithering. No one, even the people who created it, could explain to me how I needed to use it. Years latter, I read their blog post and a light bulb went on.
The fourier transform. Encountered it first in my undergrad engineering degree, it was presented as dry mathematics, with no real explanation, just threw complex exponentials at us, and pages of derivations. Years later I actually use it in my job, and through that and other material can see its beauty, and how its actually not that complicated. Some great resources like this helped a lot:

[0] https://betterexplained.com/articles/colorized-math-equation...

This is what made it click for me, can recommend it:

3Blue1Brown -- But what is the Fourier Transform? A visual introduction.

https://www.youtube.com/watch?v=spUNpyF58BY

That video did the same for me. I also like the reducible video for Fast Fourier Transforms as well as the Veratasium piece that shares some fun history.

Reducible - FFTs the most ingenious algorithm ever https://youtu.be/h7apO7q16V0 Veritasium - The Most Important Algorithm of All Time https://youtu.be/nmgFG7PUHfo

The FFT is something I still can't quite grok, for some reason.
I had the same problem. Then I took a math course where we covered the general fourier transform and it made way more sense. An the FFT is the result of a simplifying transformation based on discrete regularly spaced points and that's really opaque from the other side.
100%,those are excellent, but didn't exist when it first clicked for me. Being in university now must be great having such access to explanations that really get to the root of a concept
In case you didn't already know; Who Is Fourier?: A Mathematical Adventure is an excellent illustrated book on this.
Thanks, I'll look it up!
It took a couple years in college for me to understand entropy.

Entropy in classical thermodynamics is presented in a mysterious way that leads to confusion.

Entropy in statistical thermodynamics, however, is logical. Once one understands basic statistical thermodynamics, entropy isn't mysterious.

The book in my statistical thermodynamics class was An Introduction to Thermal Physics by Daniel Schroeder, which is an excellent book that I've referred to many times since.

I had to study entropy twice in college for different courses that were 2 years apart from each other, and I still remember this one quote I read somewhere:

  The first time you study entropy, you won't understand it. The second time you study it, you'll think you understood it until you realize you didn't. By the third time you study it, you just don't care anymore and just use it.
10 years after graduating, and I haven't encontered entropy again after the second time, so you can guess where I'm at in this quote. But thank you, for now I know how to attack it if I ever need it again.
Entropy is basically a useful quantity. It's no more mysterious than enthalpy or Gibbs free energy, both of which have also caused me confusion in the past.

To me, the issue with entropy is that it's initially presented without clear justification, so people don't know why it's important. Statistical mechanics made it clear to me that the state with the most entropy is the most likely to occur in equilibrium. (Statements about entropy representing "disorder" and whatnot in my opinion are handwavy, often lead to confusion, and should be avoided.)

I also have chosen entropy for my most memorable grokk! :)
For me, it was approaching it from the information-theoretic perspective. E. T. Jaynes' paper was what made it all click for me: https://bayes.wustl.edu/etj/articles/theory.1.pdf

Edit: but that was only after I had grokked Shannon's paper on information theory, which I felt was pretty intuitive.

The monitor model in second language acquisition. Or, more accurately, the more contemporary synthesis that it's developed into in the decades since it was first proposed.

The model itself is easy enough to grasp. But concretely understanding what it implies about how I should be studying took much, much longer.

Very intriguing! Any links you can share on this theory? A quick Google search gave me an overview, but I don’t see how this is particularly useful for second language acquisition.
The big take-away is that explicit study of grammar rules, and even vocabulary to some extent, is kind of a waste of time. They encode information in a format and region of the brain that generally isn't accessible to the areas that are used in fluent communication.

The way our brains have evolved to build up a functional language model is by observing lots and lots (and lots) of examples of the language being used for communication. Which implies that, even from the very beginning, graded readers, level-appropriate dialogues, etc. should be the foundation of a language study program and not, as most language instruction courses and apps make it, just a little bit of icing on top.

The primacy of input is also kind of a big deal, and, at least for me, it took a long time before I was willing to let go of forced production exercises such as "translate this sentence into your target language". Perhaps in part because they're so endemic to language learning resources. You can't abandon them without also abandoning Duolingo and most formal classroom programs. But the problem with these kind of practice exercises is, we now know that it's normal for there to be a long delay between when someone can comprehend a grammatical structure, and when they can use it in a natural setting. (Anyone with kids over a certain age should be familiar with the phenomenon.) Forced production relies on - and reinforces - that aforementioned misplaced encoding, and there's a mountain of research demonstrating that skill in performing those sorts of exercises simply doesn't correlate with the development of communicative fluency.

Tangentially, the transformer architecture that's taken natural language processing by storm has some interesting similarities to the leading model among SLA researchers for how language is represented in the human brain. Which isn't the monitor model itself, but might hint at a mechanism for a few parts of the model. Acquisition order, for example.

Given you eureka moment, besides what you didn't do (as you outlined), what did you do to increase your second language real world examples with feedback? Did you try immersion?
"Immersion" is a tricky word that I don't feel comfortable using without qualification.

What I like to do is spend at least an hour a day reading, listening to podcasts and audiobooks, or watching videos. A good day for me is a day when 100% of my media consumption for entertainment purposes is happening in my target language.

And that really is it.

I don't really bother soliciting explicit feedback. I suspect that it's potentially harmful because it can trigger the affective monitor. I've also encountered some SLA researchers saying the research indicates that it's not actually helpful. I'm becoming increasingly enamored of Bill VanPatten's conceit that, in a language learning context, there's no such thing as errors, there are only differences between the learner's interlanguage and their target language. Which is something that should be embraced as a natural and essential part of the process rather than a problem that needs explicit correction.

So what I do instead is just pay attention to whether successful communication has happened. When getting input, that means I'm focused on whether I understood the content or not. I want it to be a little bit difficult, enough so that I'm not getting bored, but not so difficult that I feel I'm really straining to comprehend. (There's nothing scientific to that, it's just personal taste.) When I'm having a conversation with somebody, I'm just interested in whether or not I am having a successful interaction. In a sense, what I'm trying to do is set up a feedback loop that optimizes for pure enjoyment, which, for me, seems to be a very good proxy for learning effectiveness.

To that end, I don't really intend to shit on Duolingo and practice exercises and whatnot. A lot of people enjoy those approaches to language learning, and the single most important thing is that you enjoy what you're doing.

Staying out of debt. A simple concept that took me until I was 37 to appreciate and understand.
Yes, don't rent money unless you have a very good plan.
Lie Groups... I signed up for a grad level course in my second year, and had no idea what was going on. Eventually I did my PhD on algebraic combinatorics, which works with Lie Groups quite a lot, but it took years to internalize all the ideas needed to have any intuition at all.

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

Although I still don't understand liegroups with significant mathematical rigor, I do think I finally understand why they are used in code for computer vision and state estimation. It took me a shockingly long time to understand why.

This paper was the catalyst to me finally grasping some of the details: https://arxiv.org/abs/1812.01537

blockchains as a single source of truth

always seemed like a shitty expensive database for 7+ years

What made you believe that they are not a "shitty expensive database" after all?
in the ecosystem of a single blockchain, they are the single source of truth that is an open api-like thing that no company can control

therefore it’s safer to build then on twitter, facebook, apple, etc.

the entire ecosystem may be a fraud, but if it is not then it’s incredibly enduring. i have more faith that i can get what the balance of an account is in 10 years from ethereum than i do from my banks api staying stable or open.

The use of Predicate Calculus in coming up with the Proof along with the Program.

Predicate Calculus is used to show that the path followed by a Process through a Cartesian Product space (created from all the memory variables in a Program) is the one you had in mind w.r.t. its Specifications. Suddenly you start to understand basic Set Theory, Types, Relations (Functions) and Logic.

Entropy. Both in information and in thermodynamics, and how brilliantly they are connected. The audiobook "The Big Picture" by Sean Carroll has helped a lot.
The power of an outline when writing.

Over the past few years, I've been teaching myself how to write better. I'm not talking about elementary syntax or grammar. I'm not talking about writing the traditional, American English five paragraph essay. I'm talking about writing longer pieces of prose, articles or blog posts or short chapters with word counts ranging anywhere between 1500-3000 words. On this journey of improving the craft, I realized that one of my biggest struggles was writing cohesively. Although I've been able to get lots of words on (digital) paper, eventually I'd get lost in my own web of thoughts, the article itself totally incoherent, no structure, no organization.

Constructing outlines and reverse outlines[0] has helped me tremendously. It's not easy ... but the concept itself is finally — years later — starting to click.

[0] - https://explorationsofstyle.com/2011/02/09/reverse-outlines/

This summer I read "On Writing Well" by William Zissner which was an eye opener for me. I'm far from an expert in writing clear texts, but I am definitely noticing more text which are just... big balls of blurb that don't actually say anything. All because of that book.

It sounds dumb, but this year it clicked for me how big of a difference a poorly written text compares to a well written text.

Hope your training pays off, itsmemattchung!

Zinsser's "On Writing Well" is one of my favorite books, I like how clear and concise its prose is.

I got Joseph M. William's "Style: Toward Clarity and Grace" recommended [1] so you might be interested in it. I read the introduction (I'm planning to read it this year) and with the few examples the author presents it sells the idea that prose doesn't need to be utterly complex to communicate ideas and concepts succinctly and clearly.

[1]: https://news.ycombinator.com/item?id=33601492

This is the first book I recommend to anyone who wants to improve their writing. With Minto's Pyramid Principle as a follow-on.

My top 3: 1/ Edit ruthlessly. Every single word is reduced to its simplest form and pulls its weight--it has a damn good reason for being there. 2/ Aspire to write at a third-grade reading level. Readers prefer simple writing even when reading deeply technical content. 3/ Start your most important conclusions up front, not at the end. You're not writing The Sixth Sense. Do your reader a favor and tell them the big reveal first. You can then follow through and persuade the reader why your conclusions are right.

Bottom Line Up Front and "Anything worth reading is 10% of the first draft," are the two hardest procedural skills in writing.

It's just so much experience to avoid those mistakes.

I love a good outline and can't really imagine writing anything above 1000 words without one. Getting the outline into shape really feels like breaking the back of any piece - after that it's just filling in the gaps.

Something I realised much too late was how delaying the move to the keyboard was a useful strategy. Now I mostly start with pen and paper, sometimes with a mind map if I really need to organise my thoughts, and only hit the PC once I have a pretty firm idea of what I'm going to say. I've even done first drafts in longhand, which feels like double handling at first, but there's something about that added filter of transposing from paper to computer that helps you reassess objectively what it is that you're writing.

I also love and need outlines. It’s carried forward in not just writing, but making great presentations, plans, and decision making.

Also, obligatory RiffTrax on outlines: https://youtu.be/yfcyVtD8-Dk

Thanks for sharing this!!! I know that the video clip itself is supposed to be satirical, but I actually found it quite useful. I loved it and watch all 10 minutes of it.

Out of curiosity, in the skit, do you know which book the actor is reading? In the skit, at the top of the page, the book is titled: "Making outlines and summaries"[0].

[0] Fast forwarded to specific time: https://youtu.be/yfcyVtD8-Dk?t=129

Yeah it’s great!

A couple seconds before yours, he has an index card with the title “Directing Learning 371.3”

A quick search yields the title: https://openlibrary.org/works/OL7389387W/Directing_learning

But I was unable to find a physical or digital copy available. Good luck!

Using org-mode's powerful outlining functionality has done wonders for my writing. I can ramble on and on and on, then use headings and subheadings to create conceptual "bins" and move chunks of my prose into the bins depending on topic, then smooth my words over with more transitional language so the thoughts flow more naturally.
I’ve straight up had to write essays in Excel before to break writers block. Absolute writing by outline.
I'd be interested in seeing an example of your writing process, using Excel as a way to break writer's block.
This was decades ago. But basically take which ever paragraph format intended and break it out into individual cells. So label one cell “thesis” then “point 1” etc. Then string them together in output. It was probably after being up all day and night to help explain the need for that. It did help identify points that needed reinforcement though, “I think.”
It might also be a good idea to distill transient notes down into evergreen notes to refine and review knowledge: https://notes.andymatuschak.org/Evergreen_notes

This is a great way to refine your ideas before you write and makes it easier to develop the outline as an assembly of different notes that you already have.

An Executable Strategy for Writing: https://notes.andymatuschak.org/z3PBVkZ2SvsAgFXkjHsycBeyS6Cw...

The main insights for me were: - Using notes as a way to organize one's writing (by assembling ideas together into an outline, then filling out the details) to avoid writer's block. - Creating "logs" around concepts that extract useful ideas from ephemeral observations and distill concrete insights over time. - Using an "inbox" of new ideas as a way to focus attention and perform spaced repetition of concepts. - Organizing notes via tags, backlinks, and associations/outlines rather than as a hierarchy. - Incrementally iterating on atomic concept notes to form larger chunks in memory which allows thinking about more complex ideas and recognizing patterns.

While I still do create "atomic notes" in my slip box and approach writing using a bottom up approach advocated by Zettelkaten professionals and enthusiasts, I no longer exclusively use this technique. I approach my writing not only bottom up, but top down. For me, the problem is—and has always been—with atomic notes is while its easy to create little, bite-sized notes, the problem (again: for me) that I accumulate a pile of notes, independent notes that have no connections to one another: no coherent web of thoughts.
Forward-Backward algorithm before there were all sorts of resources and explanations on how it works online.

The wikipedia page for it explains it well.

One of the biggest realizations recently for me was realizing that a nearly all of software development is basically about turning a slow, manual process into a faster, automated process. Modern CI/CD stems from a bunch of shell commands that somebody wrote and manually executed to test an app and upload it to a server. Modern automated software testing stems from humans writing small test apps and running them to confirm correct behavior. Many modern development practices stem from allowing small test apps to be written easier and faster. It's all just a giant manual process-to-automated process time-saving machine.
To add on to this, when you think in term of software development, that's already pretty meta because you skipped level 1: Formalizing any type of process. Something that is the entry level requirement to get, well, anything done via programming, is actually only done sporadicly in a lot of not-software fields.
This is a great way to understand complex, new-fangled technologies. Ask "what manual process does this speed up"?
Okay, I'll bite:

What manual process does ChatGPT speed up?

Writing peer reviews at work
The process of aggregating and formatting searchable information from many sources.

When you simply Google something, you're presented with blogs, articles, stackoverflow pages, github repositories, documentation, ... you're still left with the manual process of parsing all of the results, e.g. turning what you've read into runnable code, summarising and taking notes in a format that is easy for you to follow.

Furthermore ChatGPT allows you to have a dialog about the results. Maybe you have two equally interesting results and don't know which one to go with? Usually you'd have to do "sub-googling" in cases like this and once again parse, aggregate and format those results. With ChatGPT you can basically just ask it to expand on the previous results and help you figure out what to do.

Save humans time with a workflow.

Save humans time.

Save humans time.

In fact a well designed system I work on (database service) is built to give people sufficient monitoring & control that operators handle anything out of the ordinary until they get annoyed enough to automate it
C is basically a faster way to write ASM. And so on.
Haskel monads.
There is a common saying that if you can't explain something to someone else then you don't really understand it yourself. Monads made me realize that some things need to be experienced in order to be understood.
Complex numbers, as a convenient representation for certain operations, in the same way as we use negative numbers to represent debts.
The certain operation almost always being rotation in my experience
I didn't really understand HTTP until I started using Fiddler which was probably 10 years after I had written my first web server application. "What do you mean I can't change the headers because they've already been sent?"
Socialism

When I got out of my middle class bubble, made friends with people working multiple jobs and struggling to make ends meet, and experienced a period of financial instability; I began to realize that something wasn't working in our current system.

Then I started struggling with burnout and other issues and found that corporations were happy to just replace me. I also found that management wanted programmers to be replaceable cogs instead of professionals. At that point I started to suspect that the idea of a dignified professional lifestyle may not be true.

I observed that technology and products were getting worse over time. For example, Google search has become mostly useless and it's hard to find products that are made to be repaired. I concluded that the invisible hand and/or the price theory of value were not true.

Then I saw Republicans gain power and run up the national debt. I also observed that when wages actually started increasing the economy fell apart and the Fed started taking steps to prevent wage increases. I concluded that "free market" rhetoric was a lie.

At that point I looked for alternatives and found socialism. In particular the "social democracy" strains of socialism as opposed to those advocating central planning or anarchist organization.

>In particular the "social democracy" strains of socialism as opposed to those advocating central planning or anarchist organization.

Yes; Most people get hung up on terminology and/or specific interpretations but i am convinced this is the natural order of things for the Human species. The balance without going to extremes is what is important.

> Most people get hung up on terminology and/or specific interpretations

Because terminology matters, and some terms are quite misleading. What some people tend to miss about the "Socialism with Scandinavian Characteristics" that folks claim to like these days is that Scandinavian countries are actually near the very top rankings by economic freedom and lack of excess regulatory burden. I.e. they're actually some of the most free market and capitalist, while applying effective redistribution after the fact (leading to moderately high tax rates). So I don't get why people decry capitalism and free enterprise while praising Scandinavia as "successful" socialism? It makes no sense.

I’ve thought for years that the ideal would be a very free market with very little regulation combined with a basic income and a strong social safety net.

Let people go wild and try stuff and do whatever but raise the floor up to the point where people can recover from failure and where the less fortunate are not suffering.

This kind of “social capitalism” could be freer and less regulated than what we have now.

Sure, but the whole issue is how to get from here to there given the current regulatory environment. Adding even more red tape and wasteful government spending as in the orthodox "socialist"/"anti-capitalist" approach is unlikely to be helpful.
Observing the debate in the US from the outside, I believe the term "socialism" has to die. It's carrying so much baggage it's a major reasons the US cannot have a sensible debate over what alternatives there are to full-blown free-market capitalism.

While also somewhat contested in Germany, and not free of (some) valid criticism, I would advocate to try and use the term "Social market economy" [1].

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

> full-blown free-market capitalism

Well, full-blown free-market capitalism would be one alternative to the current US system, albeit probably not the best.

Interesting. Here in Czechia, “socialism” and “social democracy” are two very different things — one is the oppressive regime we had decades ago, the other is the current system that works reasonably well.
Czechia is still undergoing catch-up growth, much like Germany during the Wirtschaftswunder (that made their "social market economy" widely popular) and emerging countries today. When people say "social democracy" doesn't work all that well compared to leaner approaches, they mean countries where that growth process has concluded. With an overregulated economy and a high burden of all sorts of excess red tape, they tend to get stuck in a middle-income trap that leads to widespread unhappiness.
A big one for me not necessarily for “socialism” but for questioning the bog standard capitalist narrative was seeing how the quality of content often goes down when creator monetization is added on a platform.

That’s not supposed to happen. Adding a way for people to make income is supposed to incentivize better content and allow people to invest in that content. Instead what you get is a flood of addictive and sensational / tabloid trash.

… or another way of looking at it is that you do get “better” content but better is not defined in a way that truly benefits anyone.

YouTube is the most dramatic example. The quality of the whole platform tanked hard when monetization was added.

After seeing this a few times I have started noticing it everywhere. The profit motive just doesn’t incentivize quality the way we are taught that it should. It can when the incentives align but they often do not.

Another common example is actual reductions in quality to drive more spending like engineered obsolescence or “nerfing” software to drive lock in. The incentive is to produce an inferior product since that is most profitable.

This doesn’t mean I think the answer is a government bureaucracy running everything like a single monopoly. That leads to a whole other set of perverse incentives and an inability to go elsewhere.

> ... Adding a way for people to make income is supposed to incentivize better content and allow people to invest in that content. ...

What it does is incentivise more content. More is not always better for everyone, and is even less likely to be better for those who were early adopters to the platform - but what it is usually is more popular. It's just democracy in action: the largest numbers decide, and sometimes privileged minorities lose out.

It’s not quantity. It’s a change in the nature of the content. People start chasing the algorithm and trying to amp up the addictiveness of content. You end up with a cross between a casino slot machine and a supermarket tabloid.
This is not an attack on you, you are free to your opinion and to voice any or all of it to whom you choose, largely because you don't live on a socialist society.

My opinion, however, is that my parents did the right thing to drag me out of such a system and into a capitalistic one. I am watching my peers suffer.

Differential equations took a couple of years to grok. I first encountered them in high school, while preparing for physics Olympiad. I could solve basic differential equations by following the "rules" (such as for dampened oscilator), but I didn't understand what was going on under the hood. When in university I did some more math courses, suddenly differential equations clicked and made sense (and I could even derive some of the rules)
I failed calculus twice in college.

Then a summer went by before the third time I took it.

When I stepped into class that third time, everything clicked and it all felt very obvious to the point where I could anticipate where the lecturer’s equations were headed.

I stopped attending class and still got an A. I even ended up helping classmates in a study group.

I still can’t explain what that brain process was that resulted in the pieces subconsciously lining up over the summer.

The meaning of "adaptive" in the sense of evolutionary theory, and the capacity of species to evolve to extinction. Reading the selfish gene as a youth I missed the point; "an alien god" [0] and some Hanson articles got the point across finally.

[0] https://www.readthesequences.com/An-Alien-God