Comment by mstank

2 days ago

I have an open question for software engineers out there:

As someone that has never studied CS but has written basic code most of my life (accelerated now with AI), where is the best place to learn software engineering fundamentals?

I don't think there is such a thing as "software engineering fundamentals", as the fundamentals differ based on the type of software you want to make.

Do you want to make websites, or work on embedded systems?

Do you need to squeeze every OK ounce of performance out of the machine running your code, or is developer velocity more important to you?

Will your code run on a single machine, or does it need to be networked/distributed?

The thing you want to make determines what the fundamentals will look like for that area of study. There isn't enough time to learn the fundamentals of everything needed to make good software across all domains. And even if there was, you would be wasting time learning all the principles that don't apply to 99% of things you would be working on at a given moment.

  • There are tons of core principles that can be learned that largely apply across fields.

    One prime example, single source of truth for data/concepts. To be violated only when performance is meaningfully improved (denormalized databases). But when you do so, you should definitely recognize you're opening up out of sync issues for that performance gain.

    Though for the majority of code, there is no performance benefit to adding multiple sources of truth. Yet it's the most common error I see re: quality.

    The sad thing is that software engineering fundamentals and best practices never became widespread or widely taught in school prior to LLMs

This applies to more than just CS, but pick a concept and relentlessly ask why it exists until you hit a physical, mathematical, or logical reality that we cannot change/control.

For example, why do web apps use a cache? Because pulling from the main DB is too slow. Why is it slow? Because storing massive amounts of permanent data requires large, dense physical disks that must be located further away from the CPU. Why does that physical distance matter? Because we cannot rewrite the laws of physics -> (Fundamental: data transmission is constrained by the speed of light) -> an electrical signal traveling across a 5cm motherboard will universally take longer to arrive than a signal traveling 1mm from a temporary local cache.

Once you’ve found the fundamental, ascend back up to the surface concept you descended from, and that may solidify the theory a bit. To reinforce it you’ll need to pair it with practice.

I have a bachelors degree in computer science and engineering. I can tell you that throughout my 15 years of career as a software engineer (mostly backend, in fintec and JVM languages) the only things that helped me from what I learned in university were algorithms, data structure, cryptography and database internals.

Apart from that, everything else I learned when I started working (aws, design principles and coding conventions, system design patterns, domain driven design, etc.) I had to read so many books after university (pre-AI era) to learn all those things. That's what I would suggest. There are great books to learn algorithms, and data structures which is fundamental.

Making lots of things and learning what works. It's good to read and find ideas to grow, but a volume of work is the most important thing. You'll discover a lot of the ideas on your own out of need. Keeping an eye out for tools and ideas related to what you enjoy can help broaden your horizons, but time spent making things is best

FWIW, here are the books I bought over that years that people would always say you should read. I would say some of the principles might be out of date, wrong, or overkill, but a good goal is to be at least familiar with them.

The Pragmatic Programmer, Design Patterns (or some Gang of 4 book), Clean Code, Code Complete

Learn more engineering, any structured engineering. Electronics is particularly applicable, but hardly the only field that fits. Learning to find sources of error, how to handle precision and the systems of organization - all help.

I'm self-taught - could never afford much university... but I worked "odd jobs", such as assistant jobs in land surveying, welding, and electronics - and those all opened my eyes to a lot more. (I do have some university though, as well as a lot of experience... every time I line up to go back to learn more, I end up working instead, for a job I enjoy).

Oh yeah, a lot of basic engineering texts are online, or in libraries. Check them out if you can.

I don't think you can learn without doing the actual work where normal constraints apply so you can test and see the pros and cons of different approaches to different aspects of the problem(s).

You need a limited budget, limited timeline, limited capabilities in team and software tools+systems, and you need to be on the hook for support, maintenance and long term extensibility.

Software engineering is an optimization problem balancing all of the above and much more. There is no one answer to any problem, but more of a general sweet-ish spot (or more like region than spot) of balancing the competing priorities.

I would read (and work though the exercises in) "how to design programs", then follow it up with "the pragmatic programmer". that should catch you up with a lot of best practices.

Hi, CS graduate here. during my post-graduation unemployed year I have been wrestling with this question a lot. The answer is that there is none - the research on the practice of translating requirements into code has stopped in the 1970s with Structured Programming (Dijkstra) and Problem Frames (Jackson). There is a certain inspiration people had with reusable patterns of problems a la Alexander, but after some time people got lost in the Java enterprise world and started making "design patterns" (which has very much dominated the online space and I cannot search properly for general software design anymore). The second problem is with Clean Code, which to me is a really bad set of "if you know you know" hand-wavy advice that is purposefully vague, because observable, actionable advice is actually very hard to come by.

I think when people say software engineering, they confuse the high level designs (server, caching, db, etc. generally interaction constraints) and the lower level designs (modules, abstractions, classes, data structure design). There is a lot of content on the former but really few on the latter - which is some people cannot write new code, but can work on existing systems fine. Two main problems is 1. How to break down the problem at hand and 2. Organizing data into abstractions, and often creating new ones.

For (1) the advice everyone resorts to is "just do more" and rely on pattern recognition, because you can't really break down a thing you haven't seen nor can relate to what you've seen before. But there is [A Framework for Decomposition in Computational Thinking](https://www.researchgate.net/publication/334579725_A_Framewo...) that's the best attempt at it so far in my opinion. Some other approaches involve observation from ideal user flow and software ideal behaviour, or UI wireframing to see what elements should be there.

For (2), this also has the problem of organizing (which code goes into which file, which function does this belong to) and naming. For organizing data, I will recommend [The Many Forms of a Single Fact](https://www.bkent.net/Doc/manyform.htm) and the first chapter of [Data and Reality](https://cmpct.info/~calvin/Papers/Data%20and%20Reality.pdf) to see how impossible it is to create exactly correct representations in code. In areas that's entirely inside the box (e.g graphics) people don't often deal with messy representations of real world domains, but in business software they will do much more.

On the naming side, the first chapter of [Elements of Clojure](https://elementsofclojure.com/) was a really good attempt, making me realize it is way deeper into philosophy than anyone would like to wander in.

That said, the way software engineering discussion has been so far is very much influenced by the programming languages of the time (see Java), and so my recommendation is to try to learn concepts of organization independently, and see what its workaround is in your active language (e.g algebraic data type is sometimes useful, and people try to do it with sealed classes in Kotlin or interfaces in TypeScript). Once you have a good grasp of one tool (with all their warts, they all have pains), you'll find the capability to experiment.