Learning good coding practices

Started by Lord Vetinari, Thu 14/12/2017 07:38:47

Previous topic - Next topic

Lord Vetinari

Hi everybody.
I'm a mostly self-tought programmer. I learned the very basics in high school, then dabbled with it in and out throughout the years until a couple of years ago I got infected by the coding virus and started spending way too many weekends writing stuff. I believe I reached the point when I'm quite confident when it comes to the "grammar" of AGS Script, C++ and C#, or at least confident enough to find almost always a way of putting together something that does what I want it to do thanks to the ancient and revered art of the kludge.
I can only describe what I write as the coding equivalent of a splatter movie, and I really want to learn to write better, less messy programs, but I believe that keep trying on my own is not the answer; at least, it has not been the answer so far, and I can't imagine it changing in the near future.

I know that the ideal solution would be to follow a proper course, but that's not an option right now. What resources (ideally online) would you suggest me to get better in this field?

If you feel like sharing, how many of you never had a formal education in coding and computer science and managed to become a good coder? How?

Mandle

As for AGS there is an amazing series by densming that starts HERE and not only covers good coding practices but also pretty much everything you would ever need to know about using AGS if you watch through the entire series of tutorials.

As for my personal advice (and I'm a really lackluster coder at best):

(1) Proper indenting!
(2) Name variables something you will remember 6 months later even if it is "NextCutsceneCounter" instead of "NCC".
(3) Copious ammounts of notes behind // on any line you think you might not remember what it does 6 months later.
(4) Additional notes with //**********REMEMBER TO READ THIS AGAIN 6 MONTHS FROM NOW FUTURE ME*********** with additional notes below telling your future self exactly what this part of code does (or was supposed to do)
(5) Proper indenting!

AGA

Follow a Udemy course for a real programming language.  They have sales every few months, where you can get any course for â,¬10.  It's also online only, so minimal effort is required; just sit back and watch!

Snarky

Yeah, don't know about courses or resources. StackOverflow can have some good tips and discussions that you can glean useful principles from, but they want very specific, concrete questions.

As for my advice, some simple rules of thumb:

  • Indent.
  • Avoid using hardcoded numbers in your code. Use enums where it makes sense, create constants, or even just a variable. It makes your code much easier to follow (as long as you use good names, as Mandle already mentioned).
  • Don't Repeat Yourself (DRY): If you're writing the same code over and over again, make it a function! Sometimes even a one-line function can be useful and make your code more readable (for example something like, max(), min() and abs()).
  • In-fucking-dent!

And some more general principles:

Naming
Naming is important not just for variables, but for functions, types, modules, properties... everything! I read somewhere not too long ago that (good) professional programmers spend a significant chunk of their time worrying about naming things correctly. The reason for this is that comments are well and good, but it's better if the code itself is clear about what it does. By naming things well, your code will be self-documenting. Compare this:

Code: ags
trk = pick(c, 634, 2);


With this:

Code: ags
jukeboxTrack = dialogNpc.JukeBoxSelect(JUKEBOX_TRACK_MAX, eMusicCountry);


Don't be afraid to rename a variable or a function after the fact (using Search-Replace, for example).

Organizing
Indentation is just one important part of keeping your code well organized. Related functions should go together. If you can put them in a script module, do it! If some function is messy and confusing, break it up into smaller chunks (e.g. create a helper function to do part of the work). And be consistent in how you name things and how you format things!

Encapsulation
A related concept is encapsulation: wrapping up some chunk of code so that you can use it without worrying about just how it works. For example, in AGS, Character.Walk() is a piece of encapsulated functionality: it does pathfinding, movement, animation, (potentially) sound, etc., but you don't have to worry about all of that when you use it. By bundling together some useful behavior and giving it a meaningful name, it provides something really powerful and simple to use. The simplest form of encapsulation (apart from just a variable) is functions. Structs offer more advanced encapsulation, where you can offer a bunch of related functions and data. Script modules are another way to encapsulate code.

Finally, I have two general approaches for how you get there:

Refactoring
If you're anything like me, your first goal when you program is just to get something working. But once your code does (more or less) what you wanted, go back and fix it up. Does your function have the right name? Maybe it should take different arguments, or be an extender function? Maybe you can generalize it a little so that it can be used other places as well. Maybe some complicated part of it should be its own function. Maybe this code should be its own module. Maybe some part of it is hard to follow and needs comments. In this post I start from a chunk of code and show how you can turn it into a useful module through a few rounds of refactoring.

Top-Down Coding
Alternatively, it's sometimes helpful to work top-down: Just create a placeholder function that "magically" does what you need it to do. Instead of code, put in comments about what needs to happen. Once you've done that, imagine that you have the functions and data structures that you need to achieve the task easily, and put in calls and references to them. Rinse and repeat for each function. (The data structures you just have to actually set up at some point.) For example, in this thread we talked about writing a 3x3 slider puzzle, and I suggested starting by defining the high-level functions you need:

-isPuzzleSolved()
-canTileMove()
-moveTile()
-displayPuzzle()

With these functions, coding a slider puzzle should be pretty easy. Then we just have to figure out how to write them... So, for example:

Code: ags
bool isPuzzleSolved()
{
  // Loop through each tile, check that every one is in the right position
}


Knowing when to apply top-down vs. bottom-up coding, and when to refactor, is what it's all about. It's really just something that you learn from experience.

Crimson Wizard

#4
One thing I'd like to add to what Snarky said above is modularizing.
That's a smart word, but putting this simply, that's splitting larger task into smaller ones. From the coding perspective - try not to make your program into a single blob that does everything. Instead try to think how the functionality of your program may be separated into self-sufficient parts.

The advantages this gives to you:
- if the code is split into logical units, it is simplier to understand, code, review and fix;
- each part may be reused in another circumstances (similar to functions example above);
- organization-wise, you may use same "magic box" rule as with "Top-down coding" example, but in larger scale. For example, you may code a very simple version of a module that does something, and use it for early project, then gradually improve it, adding more complex functionality.
- you can TEST these parts separately, which is invaluable for a large project: this way you will be sure first part does not contain mistakes before start coding second part.

This even may make it easier for you to get help, because you may divide the task into parts that you know how to do and parts that you do not know how to do, and search solutions separately for each part.


Some arbitrary example:
You want the player to enter a phrase, split it into separate words and display these words in random positions on screen.
There are at least three subtasks here:
* getting user's input;
* splitting line of text into words;
* drawing a list of words on screen in randomly chosen positions;
Therefore, you may split the code into these three parts (e.g. functions) and deal with them separately, only passing data (original text, split text, etc) between them.

If you do this right, in the end you will have 3 functions that may be later reused, in same project or another projects.

Riaise

There are some really good tips here, thanks for sharing, everyone! ;-D

Mandle

Quote from: Riaise on Fri 15/12/2017 13:42:10
There are some really good tips here, thanks for sharing, everyone! ;-D

Yeah! The idea of just making "dummy" functions that say "This function handles how to do combat" or whatever and coding it later had never occured to me.

So much good advice in here from people who really know their shit!

This thread might do well to get stickied at some point if the good advice keeps coming!

Lord Vetinari

#7
Thank you everybody for the good suggestions! I didn't know that Udemy discounts were so big, I'll definitely keep an eye on it.
I agree with Mandle, keep them coming if you have more.

A couple of straight answers: indentation is usually not a problem for me, unless I'm typing too fast, but then I can easily recognize the mistake and fix it as I proofread the new bit of code. I put this in the same category of typos.
When it comes to naming, I'm always as explicit as possible, even if it leads to variable and functions names that may be a bit too long. I still have some issues understanding what are the standard practices in the various language communities: for example, I was told that Hungarian Notation is a bad habit in statically typed languages, yet it seems that everybody who does videotutorial on C++ uses it (or at least something that looks like it to someone like me who had no idea of what Hungarian Notation was until the first time someone - actually I believe it was Snarky on this very forum - told me not to use it).
Capitalization seems to me a bit random too. I get the capitalizeTheInitialOfAnyWord thing, but then there are subtle variations when it comes to capitalizing the first letter as well, distinguish what is a variable, what is a function, what's public or private, etc, and it seems to change from one language to the other. I came up with my own standard for that, I'll wrap my mind around what actual coders would use and expect when I'll become a bit more confident.

I don't know how you feel about it (actually I'd like to hear opinions), but to me, what stopped me from actually getting into coding until recently, was doing the sensible thing and trying to learn to walk before running. Everybody kept telling me to do small programs that focussed on one topic (like, a program all about loops, another that explored if/else vs switch statements, etc), and I always got bored and put coding aside.
Things started to get traction when I decided to say screw it, start in AGS with a project that was way too big for my own good. Since I was invested in that project, I kept coming back to it even when I was smashing my face against something I did not understand, possibly even more so because I couldn't make it do what I wanted, in a way that the small, safe, test-all-loops-programs never did.
It's entirely possible that this made me learn stuff more slowly than the sensible small programs, but to me coding has always been a way to get things done; I wasn't interested in it by itself until I started to actually understand it. Being "too stupid to understand that it can't be done", as Ron Gilbert said at the end of his Maniac Mansion post mortem, was what made me start to appriciate coding as a thing and not just the boring stuff you have to do before you can play with your shiny new game.

Crimson Wizard

Quote from: Lord Vetinari on Sat 16/12/2017 09:39:25
I don't know how you feel about it (actually I'd like to hear opinions), but to me, what stopped me from actually getting into coding until recently, was doing the sensible thing and trying to learn to walk before running. Everybody kept telling me to do small programs that focussed on one topic (like, a program all about loops, another that explored if/else vs switch statements, etc), and I always got bored and put coding aside.
Things started to get traction when I decided to say screw it, start in AGS with a project that was way too big for my own good. Since I was invested in that project, I kept coming back to it even when I was smashing my face against something I did not understand, possibly even more so because I couldn't make it do what I wanted, in a way that the small, safe, test-all-loops-programs never did.

I guess this may depend on personality, for example I am probably one of those who get bored with primitive tasks quickly.

There is a middle ground here: don't go right into a big game, but create a simple one-room or few-rooms game which explores particular gameplay mechanic(s). This won't be as boring as testing loops and switches, on the other hand you will have a very clear and close goal.

SMF spam blocked by CleanTalk