How to learn programming in general?

I suppose that logical thinking is not my problem. I wrote an A* pathfinder to move mobs in a Minecraft-like voxel game called Minetest in Lua, including steering behavior.

You can see the final result in this video

I was almost happy with the result, but I wasn't happy with the code because it was written relatively naively and straight forward.

I also wrote some websites in PHP and Python, some other game mods in Lua and a few embedded Projects in C and ASM.

One problem is, that most books, tutorials or videos just teach you the syntax of a language. They don't necessarily teach you how to write good, maintainable code. And if no one tells you, that you are doing something wrong, you will continue to do it wrong, no matter how long you try.

Also, most books have an emphasis on syntax, but I have recently learned that it is more important to understand data structures and the algorithms to work with them. If you don't know what kind of data structure you should use in a particular situation you will end in some messy, slow code.

In the last few month I realized that I'm doing something wrong. I accidentally watched a video from Andy Harris "How to think like a programmer". That opened my eyes (at least a bit).

Since then I tried to find resources to get deeper and I learned new things I never heard before. I never heard of principles like DRY because no one ever told me this. No one ever told me to analyse a problem and split it into smaller parts and write an "algorithm" to solve each of this parts.

This is how I understand "how to think like a programmer":

  1. Understand the problem
  2. Split the problem in smaller parts
  3. Write some kind of algorithm as detailed as possible, e.g. as comment in the source file
  4. Fill the space between the comments with code

Maybe a bit simplified, but I think you get the point.

But it is still difficult to "reset" my brain to use the new stuff. I still write naive, straight forward code.

I want to "restart" my coding career with Rust. And I want to focus on console tools and embedded programming. I've chosen Rust because It is the only usable language for embedded systems besides C/C++ and it is not object oriented (I cannot cope with this).

I'm not sure what has stopped you developing the aspects you seem to be reasonably aware that you want to improve. I began decades ago, like you not knowing anything about "how" to code. I just got more and more into doing it, and at each stage developed skills and techniques for tackling more ambitious things.

I never liked learning from books, so like you tend to mainly use them for reference on syntax rather than technique.

I was sent on one week long Learning Tree course after joining the Software Group of my employer. I think that helped a lot by showing me basic techniques and mental tools for designing programs, so maybe a book on that would help. I don't know any to recommend unfortunately.

Apart from that I learned by doing, but during my career was often working with people who knew more than me, so I must have picked up useful ideas and watched how they did things to an extent. Mostly though, I think I learned how to tackle different problems by doing. Sometimes you need to sit and think about how best to solve something, and maybe test out ideas in code, sometimes you can just get on and code.

These days I can often go straight to code, but I know that is because I've developed a good sense of how to do things and no longer need to spend as much time thinking. Although not long ago I did have to do that, and spent lots of time writing notes and drawing diagrams, so it always depends on what I'm trying to do.

From the sound of things you want to make a career in programming, and may have selected a Rust because of that. I'm not the best person to advise on how to get on career wise though so don't want to comment on that just now but maybe others can, if you say a bit more about your experience and goals.

Programming was most times a hobby. As I said my first experience was some Basic code on the C64. I never had a programming course or similar. I had some projects as an employer, but never in a company with other software developers. All programming skills I taught myself.

Now I work in a company repairing embedded devices, and I want to write code to analyze these devices. It's not the problem to do this with my current skills, but I want to evolve because the way I code is, as I said, naive and straight forward

1 Like

I think we should start from what your definition of "good code" is. At the most basic practical level we can ask:
a) Does the program logically do what I want. Or what some spec. demands?
b) Does the program not do what I don't want it to?
c) Does it do all that with the performance required?
d) Often for me, does it fit in the memory available?

If the answer to all that is "yes" then it is good code.

After that we get into some very nebulous territory:
e) Is it understandable to those who may come later to fix/extend/port it. Or even myself later?
d) Is it easily extendable, modifiable, portable?

Here we get into a swamp of advice about DRY, SOLID, Design Patterns etc. Countless books on the topics.

The problem is, once you get past the requirements of the here and now you are contemplating some possible requirements in the future. Which is of course very unpredictable.

I have come to the conclusion that nobody knows. No matter what they claim. For example Object Oriented Programming was de rigueur for a couple of decades. Today there is a tangible rebbelion against it.

When I started out "top down", structured design and programming you describe was the way to go: Understand, split, design, code. For a long time now overpaid consultants have been touting "agile" development instead.

I gave up hope of being a "good" programmer decades ago. It's impossible to keep up with all the fads in real-time. I just make stuff work. If I can do it so that the code reads like a coherent novel all the better. Of course I follow company standards, conventions, etc when I have to.

Bottom line is. Learning by doing. Learning by reading what others have done. It really helps if you can get to work with other skilled, experienced, programmers. In one second they can explain why doing this thing is better than doing that thing, usually by pulling apart code you have put up for review. All what they say may well be in books somewhere, but you may never find it or not understand the significance if you do.

My current advice is:

  1. If at all possible split things up into functions/modules even classes that are meaning full and possibly useful by themselves

  2. Which implies minimizing coupling between those functions/modules/classes.

  3. Minimize the amount of comments you write. Comments are extra maintenance work, change the code you need to change the comment. Which means they often fall out of sync with reality and are wrong. More confusing than helpful. The compiler cannot check them.

When you find yourself writing comments ask yourself why? If you find yourself writing something like:

// Read widget.
    // Loads of code... 
// Process widget data
    // Loads of code... 
// Write other widget
    // Loads of code... 

That may already be a clue that you have big chunks of code that could be pulled out into meaning full functions/methods

  1. Give things sensible names. For example those steps above could become "read_widget(widget)" or widget.read() or whatever. At that point the way you have created and named the functions already says what you previously wrote in comments. Make the comments redundant.

Naming things meaningfully is said to be one of the hardest things in computer science. I find that when I start thinking about what a thing should be called, different name choices change how I think about what the thing really is, or what it should be.

OK. Enough of my rambling.

4 Likes

many great points already mentioned...

i have to highlight ability to look back on own code after some years (months), wondering what kind of mor... cough enthusiastic programmer wrote that thing, and have a good nostalgic laugh after realization of ownership... (happens to me frequently)

Other than that - "clean code". Sometime many book/sources can sound too extreme or specific to domain/language, but taking a bits of advice here and there helps many times. Furry cat debugging is great clean-code helper for me, (impl rubber-duck-debugging, as i don't have the duck)... :slight_smile:

:thinking:

Would you prefer code that is clever and arcane? Surely all programmers should write code that is as straightforward as possible.

I mean, I recognize the impulse to write code that is extra clever, and I occasionally succumb to it myself, but I try to rein it in for code that I write professionally because the best kind of code to read is the code that's so utterly simple that the average bear can understand it.

3 Likes

I was wondering what was meant by "relatively naively and straight forward"?

It could mean not "clever". In which case I'm with you. Code should be a plain and clear as possible. Unless there is some pressing need otherwise. If one ever finds oneself thinking how to reduce 5 lines to 3 or get as much as possible done in a single line, please stop.

On the other hand in could mean something more insidious. One starts out coding in what seems a straight forward way, step by step. All the while thinking "OK, I need to do this so I will write that... OK, next thing..."

After some tens or hundred hours one realizes that this step by step approach has produced an giant incomprehensible mess. Every step of the way felt like the right direction but one ended up in the wrong place. In bad cases realizing one cannot even get to the end of the journey from here and it would be better to throw all away and start again.

Commonly known as "painting oneself into a corner".

The naivety here being that one did not see the big picture from the outset. One wanted to climb a mountain but in those little steps, up hill all the way, ended up stranded on a lesser hill.

I have no idea how anyone can be taught this, it comes from a lot of experience.

It leads to the oft' given advice "be prepared to write it twice".

4 Likes

I found the PingCAP Talent Plan to be a good way to improve my style. I would work through an exercise and then compared my code to the provided solution. That approach proved to be better than just reading code, because my brain was deeply involve in the solution.

1 Like

I care a lot about writing code that is easy to understand, and this thread reminds of an experience from my job I'm not quite sure what to do about. I wrote an API that one of our customers is supposed to use to transfer various types of user data between their and our systems, and it has some ... uncomfortably complex requirements. The first MVP I wrote was a spaghetty-mess, but I rewrote much of it in a manner with several smaller self-contained pieces of code that should individually be easy to understand, and then I implemented the API by composing these pieces.

However since I work part-time outside of the summer vacation, another developer has taken over the main part of the project (I wrote it during his summer vacation). As far as I can tell, he has no trouble understanding each individual module in isolation, but when he has tried to make a larger change, he had a hard time figuring out where to look for the problem. I guess the composition is what is difficult here?

It's not like this is a bad developer either. I really wish I knew how to avoid this, because I really do value understandable code quite a lot.

3 Likes
DISCLAIMER

The following paragraphs express my personal opinion, experience and belief. I am not going to be able to prove anything what I write and if you disagree, I'll gladly accept that as-is, because having differing opinions is natural and the basis of our society. I am not interested in any heated debates, that tend to escalate into "religious wars". Also, please try not to change my opinion to your opinion. If you can provide scientific proof, that the conclusions I ended up with are wrong, by all means do that. That's not the same as trying to force your opinion on me, because opinion leaves space for diversity and interpretation while scientific proof is absolute. I'll just ask you to be civil and not condescending, if you do so. Thanks for reading this!

A good way to assure code is maintainable is to provide a complete list of requirements of the code, as well as the reasons behind the way it was written.

After all, we cannot read other people's minds just by looking at their code. Different thought patterns make it hard to interface with other people's code, unless you've worked with them long enough to grasp how they tend to write the code to be able to navigate through it and understand it without documentation.

Many solutions exist to any given problem and even if you solve the same problem in the same way, there are many "accents" for the same programming language. While we have a standardized way of formatting our code via rustfmt, we do not have a standardized way of organizing our code; that is to say, we don't communicate with a common accent.

While it is technically possible to automate code organization, the tool that comes closest to it is clippy, but that tool doesn't make any changes to your code by itself and is not nearly as aggressive with normalizing code as it could be.

I guess, the next step after having agreed on a standard way of formatting our code and having a tool which enforces this standard is to agree on a standard for organizing our code and make a tool that enforces it.

People will likely fight for their one and true way of code organization, just as they did for their preferred way of code formatting. In the end, after exhaustion due to all the fighting, we get to the point where we'd rather just have homogenous code, always formatted the same way and organized the same way, not having to learn several hundreds of different accents to understand every bit of larger code base there is.

Personally, I just want to be concerned with the important, actual problem at hand and not having to learn how someone's code is formatted and organized, first. That's not the purpose of programming!

1 Like

Well, maybe one reason for this is some code I've seen in my company (not written by me). This is literally spaghetti code, written 10 or even 15 years ago and hard to maintain.

This code needs to be refactored or even rewritten. But this will never happen because it costs time and time is money. The fact that the maintenance and the bugs also cost money is not taken into account.

I want to be able to write better code than this. I know that this takes time and a lot of practice, but it takes longer if you have no direction and just stumble around.

I agree with @Phlopsi about everyone solving a problem in a different way. In a former job, a group of us was asked to vectorize some scalar Fortran. The task was mostly mind numbingly dull, so we amused ourselves by trying to tell who had written a given routine. By the end of the project we had gotten pretty good at it.

1 Like

Yes. That word "composition" is a trigger for me. It generally means that whatever follows next is totally incomprehensible.

First order of the day is that I want to push data into your API, have it do whatever it does, and get data out.

I don't want to "compose" it with anything.

This first hit me a decade ago when some C++ library we were using required deriving a class from whatever base class the API had and implementing a bunch of methods to get it to work. All totally unnecessary busy work.

I guess there might be a new generation weaned on "Functional Composition" now a days for whom this all comes naturally. I have not met any yet.

Or maybe I have not understood what you are getting at....

In this particular case composition means to read the request data into some sort of object. Read some data from the database into another object. Combine the two into a combined object. Write the new object to the database, and finally create some sort of response, also based on the combined object.

The composition refers to having a file that calls out to a bunch of otherwise independent modules, tying them together.

1 Like

Aristotle said:
"Practice or practice is an action, the purpose of which is itself, is superior, for having an end in itself, and therefore sufficiency.
Theory is also practical; they are opposed only as to how much theory is supreme practice, unlike what is only practical, but does not become theoretical."
In this sense, I think you should advance the theory, perhaps a data structure course is a good way, the book "The Art of UNIX Programming" may be another, or an elementary course like this https://www.coursera.org/specializations/computational-thinking-c-programming

I think I found a good example for what I mean with "naive programming"

If I had to solve this problem, I would try to parse the actual word instead of reducing the problem. It's not wrong, but also not good.

Nice little problem.

Surprisingly he does not make the observation that the numbers he gets in each cell, as worked in his spread sheet, form Pascal's Triangle: Pascal's triangle - Wikipedia. Which gives one all kind of interesting ways to think about the problem. As people have been doing for 2000 years or more.

These kind of programming puzzles are rather like the questions in maths challenges. The first thing you have to do is pull the problem out of the words the question uses. Those words are often thrown in as a "blinders" to throw you off the scent. As in this example.

The mathematician Po-Shen Loh gives exactly the same advice as this guy, look for different approaches, play with little examples, look for patterns, to kids tackling maths problems.
"Daily Challenge with Po-Shen Loh": https://www.youtube.com/channel/UCf78EJOm4wQ4xXwSS15PuxQ

Po-Shen Loh likes to say that one should not be afraid of maths, it's just "thinking". And so it is with programming.

1 Like

Thanks for your tips. I will start from scratch and I will try to build solid experience and knowledge

2 Likes

That's the spirit. Keep at it.

1 Like

Elm really helped me understand how to structure programs. Rust is similarly well-designed, so both languages nudge you towards doing the right things. The benefit of Elm is that it is much smaller and specific, so it's much easier to go beyond the syntax and constructs, and start thinking about how do to things better.

I agree with the others that the best way to learn is to make something. Only when you have felt the pain, do you understand why certain things are helpful. For example long variable names might feel unnecessary while writing, but once you've read your code from a month ago, you'll see why long names are absolutely worth the extra second of typing.

Incidentally, the Elm community puts out quite a few very informative talks and resources. Of the top of my mind, I can recall that The Life of a File and Making Impossible States Impossible helped me understand modules and types much better, not just in Elm.

2 Likes