Dota 2 - Advice on programming together
As someone with a bachelor of science in computer science (almost), we spent quite some time on how to organize yourself when working on software, to make it easier to work together.
This document will contain my advice and experience on this subject. This topic is obviously completely subjective and the best way is different for individual and every team, still I hope
this document will have some value in development, and if I ever work in a team I will make sure they have read this.
So let me start by giving a list of what benefits you get out of your programming by following this document:
PS: This might seem like extra work, and it is, but in the end you benefit from it. Usually when I'm working alone on something personally I don't use most of the things in this document,
but when working in a group this is VERY beneficial.
- Readability - not only for others but also yourself (some of you might recognise 'I wrote this but I don't know what it does anymore')
- Maintainability - kind of goes together with readability, makes your code easier to maintain
- Extendability - it will be easier to add new things to your code, not just by you but even by people that haven't seen it yet.
- Probably some more stuff, but you can make that up yourself.
This one seems pretty straightforward, and it is. Before starting you should have done the others points in this document and you should have a clear division of who does what.
Most importantly though, you should set a number of deadlines. I realise this is hard to do correctly, but you should have deadlines. Nothing is more annoying than having
multiple people having to stop working on a project because you are waiting for one person who hasn't made his deadline. Tip: Set multiple deadlines for certain steps in the project,
it helps structuring the progress of the project and gives a clear timeline you can use to check progress. It is entirely possible somebody (maybe you) can not make his deadline for
whatever reason, the important thing is that you let your team know ahead of time, so they can help you or adjust to it.
Use one, any kind.
If you have no idea what a repository is, read my dota 2 version control guide.
This might be the most important paragraph in this document. I can't even begin to explain how important comments are when working on the same code with multiple people.
Not only will comments help others understand the code you wrote, it will also help you when revisiting old code. This especially holds for larger amounts of code. Read
this paragraph. Do this paragraph. It will help, even if you do it when working on a project alone.
Good practice when commenting is asking yourself about sections of code 'is it immediately obvious what this bit of code does?'; If the answer is not 100% yes: Add a comment. This
does not have to be a long comment, but just something like '//Changing this variable to hold the caster instead of the target now' will do. If this means you have to add a
comment every line: do it. If this means you might not have a comment for multiple lines: fine, as long as the function of those lines is obvious, or described by a previous comment.
So far for the obvious part, now come the 'secret tricks' of the academic programmer. This might seem like just a lot of extra work, but if you have (tens/hundreds of) thousands lines of
code this will help, trust me.
Each function should have a contract, no matter which function we are talking about. The level of detail should be defined in the coding standard, ranging from simple to elaborate. The
point of a contract is that whatever function is below that contract does not break it. The absolute minimum is a description of the function, what it does basically. I like adding a
definition of the input and output to that, which clarifies it a lot. On top of that I like adding the author, so you know who to ask who to ask when the function is not clear or
incorrect. A contract with a description, input, output and author can look like anything depending on your coding standard, but I would write it like this:
function Power( a, b )
Now the nice thing is that contracts ALWAYS hold, so other contributors know what the function does in case they want to use it, and on top of that they get a guarantee that
as long as they adhere to the input specs, they will get the correct output as defined. If this is not the case, they know what author to blame. Some more things you could add
are pre- and post-conditions, the variables this function modifies, or more extensive descriptions of the different parameters. I do not recommend putting in all those, you will
not be happy (unfortunately I know this from experience too, this is nice but too much is very annoying).
This is not used a lot in dota 2 lua, but it applies to general programming:
Not as important as function descriptions, still useful. At the top of each class add a little description of the class, what it is used for, and who the main author is. You can do this
in a few lines only.
What you should always do when working on a project with multiple programmers is defining and agreeing on some kind of standard. This standard can be as detailed as
needed, but here are some things you probably should define (you can add anything you like to these yourself!):
Tabs or spaces for indentation is a common definition in coding standards. Another one is the maximum line width (approximately, this increases readability of code on different resolutions),
usually defined in characters. You can add whatever you agree on on top of these first ones if you feel there is a need for it.
Some related guidelines:
- Use descriptive names for variables and functions
- Try avoiding code duplication, if you find you are repeating lines of code, move them to a seperate function
- Watch out for name collisions
Like I said in previous parts it is important to define how you comment. Regular comments don't really matter, but you should make solid agreements on the format of your
function contracts and class descriptions. Define exactly what should be in there, and save a lot of hassle.
I'll leave you an example of some code written according to these guidelines.
function Vector2:Add( vec )
self.x = self.x + vec.x
self.y = self.y + vec.y
return math.sqrt( self.x * self.x + self.y * self.y )
function Vector2:SetLength( length )
local factor = length/self:Length()
self.x = self.x * factor
self.y = self.y * factor