Version control makes sense whether you are working alone or with a team. It should be a mandatory practice. But you won’t be able to enjoy the benefits of version control unless you know how to do it right. If you’re working with a team, it’s essential that you all know how to do it right. This article will help you get started.
[Editor’s note: A year or more ago, SubVersioN (svn) integration with the GM:S IDE was buggy and caused a lot of developers who wanted to work with it to manage file check-in/out outside of GM:S using a SubVersioN client like TortoiseSVN. I am not aware whether the issues with the svn support features in GM:S have been fixed or not, but I’d sure hope so. (I’ll revise this once I’ve tested with it again, hopefully soon. It’s on my to-do list.) In the meantime, test out GM:S’s svn integration features, and see whether you can work within GM:S exclusively, or if you’ll be better off managing the version check-in/out with an external svn client.] If you use a version control system other than svn, such as git or mercurial or cvs, you’ll have to use an external client. GM:S currently supports svn only. As of October 2014, GM:S supports git and Mercurial as well.
Get set up
To get started, learn how to use svn, independently of GameMaker. Google for “subversion tutorial” and learn the basic concepts of version control. This article will cover a lot of the concepts, but there are plenty of good tutorials out there that you should look at for the specific technical details for how to use svn, whether from a client or from the command line. If you’re not aware of the official GameMaker help documentation, you should be. Read the articles covering the svn integration features in GM:S. GameMaker Community Forums user GameGeisha has created a good tutorial covering the setup process, as well.
Make sure that everyone on the team understands the fundamentals of version control. Often, with a new team of inexperienced developers, only one person on a team (if that) will really understand it, value it, and believe in it. It’s important that everyone be on board. Once everyone’s familiar with the basic concepts, get everyone set up. Until a team member is competent, don’t give them access to the repository. It may even be a good idea to have a “play” repository where you can train new team members and let them get the hang of things without messing up your real projects. Have them hand off their work to someone who does understand version control, and have the competent version control user manage their contributions. This gets tedious quickly, so it’s a good incentive for the competent users to mentor and teach the newbs. Don’t babysit too long, or tolerate team members who refuse to learn. If a team member can’t handle version control, they’re off the team. Version control is a core competency.
Check-in/Check-out basics
Only work in files that you have checked out.
Don’t edit a file without checking it out of the repository.
Don’t start editing a file until you have downloaded the latest version of it from the repository. You may have an outdated version of the file locally, so always bring down the latest immediately prior to checking out, to ensure that your working copy is the latest version — before you start working on it.
Test first, then check in. The developer working on a change should TEST the changes they’ve made BEFORE checking them back in to the project, and do NOT check in work-in-progress that isn’t tested and working. Don’t check in simply because it’s time to go to lunch, or the end of the day, and you feel like it’s important to store the changes in progress in the repository in case the developer’s machine crashes. That’s not the point of version control. Developers should still use a local backup.
The source control repository is for managing changes in the project, not a disaster recovery backup. Unless the disaster is developer error resulting in a bad check-in. ;-)
Check in chunks. The developer should check in changes they’ve made in logical “chunks”. If your “ToDo” list has 3 unrelated items, don’t check out the entire project, do ToDo1, ToDo2, and ToDo3, and then check everything in. Do a check out for ToDo1, implement it, test it, check it in; then do a checkout for ToDo2, implement it, test it, check it in. Then do a a checkout for ToDo3, implement it, test it, check it in. This way, if anything needs to be rolled back, only the changes related to that item on the ToDo list will be undone, and you won’t have to re-do the unrelated items that you checked in as separate changes.
Documentation. It’s mandatory. When checking in, the developer doing the check-in should write up detailed notes describing the changes. Many programmers who have a strong concentration in science and math do not have “humanities” skills like writing, or don’t enjoy it, or just don’t have a good grasp of who the audience is and the level of detail needed. But documentation is important. Don’t skip it. It’s critical to the success of your version control that the check-in notes describe the changes that were made, in detail. “Fixed some bugs” is not detail. “Fixed Bug #12345098 by doing 1) […], 2) […], 3) […]”is much better.
It can be hard to summarize (or even remember) all the changes you made to the project when it’s time to check-in. If you find this to be the case, it’s a sign that your “chunks” are too big, and you should consider whether you might be better served by making your ToDo items smaller. But also, don’t wait until you’re done coding and testing to start writing documentation. Write your check-in notes as you are coding. Think of it as live-tweeting what you’re coding right now. Dump your notes into a text file as you go, and the “tedious chore” of documentation will already be done when you’re ready to check-in. Give it a quick proof-read, edit for clarity and completeness, and you’re home free.
Never be in a hurry to check in. Do it right, or don’t do it. The repository should have an overseer who reviews all check-ins. If someone checks in work incorrectly, either reject it, or roll it back and make the offending developer do whatever they need to do to make the check-in right.
Avoid merge conflicts at first. When you’re new to working with version control, the easiest thing is to lock files when they are checked out, so that only the developer who has the file checked out can work on it. While enabling locks is possible in some version control systems, it’s like training wheels. It creates bottlenecks in the development process, and slows an experienced team down, and should be avoided once the team is comfortable doing so. (This comes later, when the team is more advanced and is ready to deal with merges and resolving conflicts).
With locked files, while the project resource(s) in question is/are checked out, other collaborators cannot modify and check them in. This means that only one developer can work on any given resource at a time. When a developer is done implementing the changes to the file(s), they should check in their changes. Then the file(s) they were working on become unlocked, and are available for other team members to check out.
When the developers are comfortable enough with using source control, you might allow them to work concurrently on the same files. At this point you’ll need to be well versed in GML and code architecture, in order to effectively resolve merge conflicts. This is rather advanced and perhaps unnecessary for the non-professional.
Establish a “house style” for the team to follow
If more than one developer is working on resources that interact with each other, they need to coordinate with each other and make sure that their efforts are not redundant and are compatible. This takes some discipline and unless the team is agreeable to coding to a “house standard” it can lead to unproductive arguments about whose style is best.
Try not to get bogged down in these “holy wars”, but do try to have a good style that is easy to follow and that everyone understands and does not feel (overly) constrained by.
There are typically any number of ways to implement any given feature. Some are better than others, and some may even be wrong. Because project assets can be inter-dependent on each other (such as an Object has a Sprite, or uses a Script, or references some other Object in its code), the team should have a good understanding of programming style/naming conventions, and follow them faithfully, in order to avoid writing code that is incompatible with another contributor’s.
This is especially important in GameMaker projects, because there’s no concept of encapsulation to protect an object’s internal variables from outside meddling. Everything in a GameMaker program is essentially Public, and object A can directly access and modify variables belonging to object B at runtime. There also are no refactoring tools built into GameMaker, which would make changing names of things like variables and assets easier.
Following a conventional coding style is also important in order for multiple programmers to be able to understand each other’s contributions to the project, so that they can work with them, or work on them if need be. If two developers build equivalent things differently, it can result in a messy, disorganized project that is no fun for anyone to work on. Strive to work in a consistent style that everyone can understand.
- Plan out project architecture, and design patterns for building objects ahead of time, or at least before you get too far ahead of your team. Don’t overplan, but do design and coordinate.
- Agree to standard variable names or naming conventions.
- Code in a manner that expresses its own intent. Expressive code shows its How without much need for additional explanation.
- This does not mean that you don’t need code comments. Comments should provide Why, and additional context or cautions. You can also explain any assumptions the code makes, anything it deliberately doesn’t do, mark “todo” notes for things you intend to implement later, but haven’t yet, and “bug” notes that say where you think a bug exists, but haven’t fixed yet (or haven’t fully understood yet). You can also sign your comments with your initials, so you know who wrote what in case someone reading the comment has questions. Comment effectively.
- Resolve philosophical conflicts amicably, effectively, and quickly. Don’t obsess over doing things “the perfect way.” The goal should be to make steady progress with the project, while incurring as little “technical debt” (stuff that makes future project maintenance more of a pain, that you elect to fix later) as possible.
- Don’t let the standard hold you back. If the standard is holding the team back, fix the standard. Don’t violate the standard. If you need to, build a new project branch to prove the concept of some proposed change to the standard code style, and present it to the team for consideration and adoption.
- If you struggle with coding to standard, use this method:
- Use comments to pseudocode your program until you’re sure of correctness of the algorithm
- Backfill the comments with functioning GML code, iterating as need-be toward a complete solution.
- Make the code expressive and conform to the style.
- Eliminate as many comments as are no longer needed.
It doesn’t have to be the perfect style, but a good style should have beauty. The project shouldn’t cater to the least common denominator, but should try to elevate junior programmers as they gain experience, until they are capable of contributing at a higher level. But very advanced programming techniques may be best to avoid, or if they are not avoidable, then these areas of the project should be designated off limits to those who are not ready to work on them.
In general, programming should strive to be: a) correct; b) understandable; c) fast, in that order. Unless the code is in a performance critical area of the program, you should choose code that is easy to understand rather than fast at runtime. Optimize your code only if you must, and try to preserve understandability as much as possible.
Branching
Learn when to Branch a project from the Trunk, and how to manage Branches.
A good reason to create a Branch is if you’re about to add an experimental new feature to the project, and aren’t sure whether it will be successful. Perhaps you’ll discover during playtesting that the new feature is not fun. Or perhaps you’re not sure about the technical implementation for the feature, and are trying out a new technique for the first time to see if it is suited to the task. This is a good time to create a new branch for the project. If the code experiment is successful, the branch can be merged into the trunk; if not, the branch may be discarded.
Create a Branch, work on the new thing in the Branch, and if/when it’s deemed worthy of merging back into the trunk, do so. Some branches may need to remain outside of the trunk forever — perhaps you’re building a variant program based on some common code in the trunk that you’re re-using. An aftermarket extension, or perhaps a sequel to the 1.0 game. Or a version with code customizations needed to run on a new target platform. You want to keep a branch for the 1.0 project so you can continue to maintain it, but you want a new branch for 2.0 so that the changes you make for 2.0 don’t affect 1.0. Some branches may eventually need to Fork into their own repository (suppose you take the core platforming engine from your first successful platforming game, and you want to make a new platforming game with it, but it’s otherwise completely unrelated to the original game.)
Version control all the things!
Version control isn’t just for code! Documentation, art and sound resources, data, marketing materials, end-user instruction manuals, business contracts… if it’s a file, it can be managed with version control. That’s why I call it version control, and not source control. If it goes through a process of iterative development, or if you would like to be able to easily manage previous versions so you can see how it evolved, or go back to an earlier version, it should be managed with version control. Once you “get” version control, you’ll learn to love it, and want to use it for everything.