Hacker News new | past | comments | ask | show | jobs | submit login
Ask HN: What is the most enlightening concept that changed how you program?
23 points by newsoul on June 23, 2023 | hide | past | favorite | 32 comments
Assuming that you are still learning and you learn from new experiences or concepts, what was the most mind blowing concept you learnt that totally changed how you approached programming problems?

Edit: Provide pointers to where others can learn about those concepts




Compression-oriented programming [1]. It is not something that "totally changed the way I approach programming", but more like something I was doing intuitively and reading about it made a lot of sense and provided a framework for why it is appealing. Also, keep in mind that I am not a real programmer in a sense that I mostly write scripts for data analysis.

The gist of it: you program procedurally without thinking too much about the code and only add "compressions" when the need for them arises. The compressions can take various forms like encapsulating data types within objects, behaviour within functions, extracting often-repeated operations into separate libraries and then loading and referencing them when needed, etc.

[1]: https://caseymuratori.com/blog_0015


Lambda calculus. Applied functions and recursion. Everything else is syntactic sugar on imperative do.. lists.

Typed arguments as strong pre requirements to intent, and the cost/benefit of typechecks.

What do I actually do? Boring imperative python3.

I wish I'd learned LISP as my first language. It was literally a left door right door choice to do Fortran on cards, and then Pascal. Two people teaching the induction classes. I went through the Fortran->Pascal door.

If I'd turned left, I would have entered the other cohort who started with LISP.


Defining data structures first and then the rest of the program as operations between those data structures. If possible using types, and keeping as much as possible pure (no side-effects). Makes it easier to reason about, and to check for correctness.


Minimising complexity by reducing the number of paths (branches, nested loops etc.) through your code.

Thinking of the stupidest way we can get it done and then ruthlessly refactoring until the resulting algorithm / system is as simple as possible.

Edit: point 1 comes from A Philosophy of Software Design. One of my fave programming books of all time.


Kevlin Henney - "when you introduce concurrency into a non-concurrent environment what you do is you change the laws of physics of your program, you change the nature of time"[1]

This is a profound insight. It totally changed how I think about the nature of multithreaded and multiprocessor based programming.

Also from Kevlin - Refactoring to immutability[2], which showed me exactly why you would want immutable "variables"

[1] https://www.youtube.com/watch?v=Hi6ICEVVRiw

[2] https://www.youtube.com/watch?v=APUCMSPiNh4


Foregoing perfectionism, elitism, and fashion in favor of code that Just Works. Functional is nice, concurrent is a nice, sometimes necessary transformation, "compression-oriented" sounds cringe and complicated-pilled (but I'm sure it's nice). But I'm not here for nice things, I'm here for profitable things, and profitable things need to Just Work. They don't need to be functional, or algorithmically perfect, or nerdically sound.

Procedural is fine and simple and it works. You can refactor later if you have the time and money.

Related listening/watching: Jonathan Blow.


Let me play devils advocate here for a second.

I have seen a lot of videos of Jonathan Blow. IIRC he created his own programming language to code in. That could be interpreted as pretty "nerdically sound" and "perfectionist" by many.

My point is, I agree with what you said in spirit, but the way you phrased it could be used to justify spaguetti code, too much technical debt and, ironically, fashion-based decisions (we need to just make it work, lets choose x).


1. type safety from typescript avoids lots of bugs before running my code

2. linters like eslint forces best practices and readability in my code

3. security is learnable from online sources like latacora blog and owasp cheatsheets

4. reading latest docs and github issues helps a lot in debugging.

5. reading readme file and getting started guides is more reliable than watching video tutorials.

6. videos however are good at getting comparisons and seeig presentations of ideal use cases of particular tool software or database or framework


Functional programming. Clojure changed how I write C#, Python, and think about storing data.

I now think much more about immutable, append-only data stores as a result.


Functional programming - I realised that state is one of the greatest enemy and that it is possible to write programs without it.


I'm a grumpy old programmer now but one of the early enlightening concepts I learnt was recursion and how it applied to so many problems. As the saying goes, "To understand recursion, you have to first understand recursion" :-).


YAGNI and WET - You Ain't Gonna Need It and Write Everything Twice - Copied code leads to simple bugs; premature abstraction leads to heartbreak.


> premature abstraction leads to heartbreak.

Anything that is “premature” is per definition incorrect. So there is no information in that sentence.

Having said that, a lack of architectural abstractions leads to badly designed code with a WTF count reaching the heavens. Have you ever seen UI code directly relying on a specific flavour of SQL implementation and database? That’s an example of how a lack of architectural abstractions leads to spaghetti code. Or how about the domain logic directly relying on a specific UI framework? Another example where an early architectural abstractions would have saved years of maintenance work. Again something I see all the time in the wild.


Not op, but let me rephrase it:

leaky abstractions create more trouble than is worth it. If your abstraction does not fit the problem neatly do not abstract it.


Leaky abstractions are bad abstractions. So what you are basically saying is “don’t implement bad abstractions”. Ehhhh right. I think we can all agree to not implement bad things. There is no insight or enlightenment there IMHO.


As I was writing my comment I was picturing this answer, and Socrates strategy for discussion, making others define and define ad infinitum. Fair enough. Quoting you:

> Having said that, a lack of architectural abstractions leads to badly designed code with a WTF count reaching the heavens.

> a lack of architectural abstractions leads to spaghetti code.

> domain logic directly relying on a specific UI framework?

If we are pedantic: lack of architectural abstractions, spaghetti code, and domain logic tied to an UI can also be interpreted as simply bad, so no information in either of our posts :)

Of course, I am joking. Saying that I think leaky abstractions are bad, or you saying that no abstractions are bad is new information (it reduces uncertainty). It tells us that we think that x is bad.

We can agree that bad abstractions are bad. In this thread, abstracting to early, or having leaky abstractions or not having any abstractions are just 3 positions on the topic of what a bad abstraction is.

I bet we could much easier agree with a specific example. In my experience all "big principle" discussions improve when we focus on a specific example. Otherwise people are thinking of different scenarios.

In any case, I can think of scenarios where each of those 3 positions is right, so I do not really disagree with any of them.


Yeah I think we pretty much agree.

An example where a lack of abstractions is always bad IMHO is when the UI is directly depending on a specific database implementation. For example, dialog callbacks directly using the Oracle dialect of SQL to do business logic. I see that all the time in the wild and it makes the code super hard to improve, test, and maintain.

Another example is the domain logic directly depending on an external framework or database. A simple abstraction that reverts the dependency would make the code so much more maintainable and flexible.

Those examples are but two of the many I see in the wild all the time. Which makes me believe that often, in practice, the lack of architectural abstractions is a big problem.


> premature abstraction leads to heartbreak

Could you please elaborate on that? I'm not sure to understand.


The devastation of realizing that the days you spent working out the perfect class hierarchy or other complex design to handle every case was a complete waste of time and what you made is an over complicated mess that no one else can understand or use, and you could have just copied a method from one place to another and added a couple of ifs or arguments


In my experience abstracting before you know all the use cases is generally the problem.

Write the simplest abstraction first, then as the system develops combine similar use cases into useful abstractions for clarity


Buisness world startup equivalent would be know your market audience.

Start out to broad and wind up wasting resources that could be put to better use.

Start out to narrow focus without knowing where need/$$ to keep going is and wind up wasting resources. Idealy, the narrower focus permits getting to the 'wasting resources' point faster.

Hiding the details via abstraction when details are still in development/need to be viewable & readily available defeats the purpose of abstraction making things simpiler/easier.


We had moved to fully typed python using mypy and ruff and using framework that highly benefit from it https://litestar.dev . It greatly improved our code quality , our understanding of python and how python work . At first it is tedious and felt over engineering but it's totally worth it.


Using state machines as enum + switch. Massive simplification for protocol based things and solving edge cases during communication.


Avoid deep nesting by testing error case first and exiting/returning immediately.

Instead of :

  If foo then
    bar
  Else
    baz
  Endif
Now I write:

  If foo then
    bar
    return
  Endif
  baz
Exit early makes the code easier to read and understand


IMO the example you provided is an anti-pattern. There are better ways to avoid deeply nested code, but it depends on the programming language.

I try to make each method/function to have a single entry and a single exit.

Cond, case, pattern maching in function heads, decision tables, and syntactic sugar in some PLs, allow you to flatten the nested code / branchinbg / conditionals.


Maybe. Or maybe it is not obvious with a small example. Not sure about the anti-pattern, but I'm sure an anti-conventional coder ;-)


Dependency injection & Inversion of control.

Technically 2 concepts but they’re highly related.

Basically, allow the consumer of a component to pass in something, rather than the component needing to contain that logic.

It’s great for front end systems, but also apples to backend and general systems programming… an invaluable concept


Category theory.

Basically, it boils down to this: F(A . B) = F(A) + F(B)

(F,A,B, ., +) could be anything which you found suitable.


Normalised tables.


Can you elaborate? Unless you mean as in DB normalisation.


the most mind blowing concept was realizing everything I have learned during my 10+ years of coding will be rendered obsolete by an AI in next 2 years :)


yes, but it'll still take until tomorrow to realize what was needed the day before.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: