CMU 15-112: Fundamentals of Programming and Computer Science
Class Notes: Large Project Design and Documentation
- Design
When you're working on a large project, you can't jump straight into coding. First, you need to determine what your project should do. Design is the process of purposefully planning what a project should look like, and can greatly help in guiding coding later on. We'll cover a few design methods that can help you plan your projects.
- Competitive Analysis
Competitive Analysis is a method most often used in marketing, but it can also help you generate a project idea by comparing similar projects that already exist. Looking at the competition will help you identify what's already been done, which should help you identify what can make your own project unique.
When performing a competitive analysis, the main steps are:
- Decide what the main idea of your project is.
- Identify 2-3 competitors that have a similar idea or similar features to your project. These should all be projects that you can interactively explore.
- Identify 5+ dimensions or features that you can compare across the competitors. You should choose the dimensions based on variation; for example, if all of your competitor games include a pause button, it isn't interesting to compare on that dimension.
- Make a table of competitors x dimensions, and fill it in based on your analysis of the competitors. This can be done with numbers (1 to 10), or with qualitative descriptions (bad to good), or with anything else that makes sense based on the context.
- Analyze the table, looking for trends and points of interest. Then summarize which features your project will need to have and potential gaps in the market that could make your project unique.
In this class, you may wish to use our Competitive Analysis Template when performing this design method. - Storyboarding
While Competitive Analysis can help you decide what should go into your project, Storyboarding can help you decide what the experience of interacting with it should feel like. Storyboarding is another design method, but one focused more on the user experience than the features in the system. Telling the story of your project can answer questions about why people would use it, and what their goals with it would be.
In drawing a storyboard, you want to focus on three main elements: the user's context, the system interaction, and the story being told. The amount of time spent on each of these three elements will vary based on the project you're developing. If you're building an app for a specific purpose, you'll want to focus on why a user would download the app, and when they would interact with it. But if you're planning on building a game, the context is less important than the interaction with the game itself.
The best way to learn how to draw a storyboard is to see examples of storyboards. You can find general guides to them here and here. When you make your own storyboard, you might want to use our Storyboarding Template to begin. - Prototyping
Once you know what you want your project to feel like, you need to decide how to actually set up the system for user interaction. Prototyping is a good way to try out several variations on a system's design without actually needing to build the whole thing. This can save you plenty of time when prototypes demonstrate clear problems that need to be fixed.
Prototyping can be done at varying levels of fidelity. At the lowest level, you can make paper prototypes to try out different system layouts with no coding at all. At a higher level, wireframing lets you add more structure to a prototype, but with a drag-and-drop interface instead of functioning code. You can also make functional prototypes with basic graphics, like a Mario game where Mario is replaced by a red rectangle. This can help you get the core functionality working without having to worry about the visual appeal.
- Competitive Analysis
- Documentation
- Structural Planning
When working on a large project, it helps to plan out how you'll organize your code before actually writing the code. This avoids time-consuming refactoring later down the road.
First, think about how you can separate out the different elements of your project. Is there anything that could be naturally organized as a class/object, or a group of functions? Which variables and functions should be stored where? Would inheritance or well-defined helper functions be useful anywhere for reducing code repitition? Being able to turn a set of needs into an organizational structure is the extremely important in large projects!
As an example, assume you want to build a computer game that has three types of characters: players, friends, and foes. All characters have a name and a location and can be moved to new locations. Players and foes have health levels and can attack other characters. A friend can defend a player but can neither attack nor be hurt by an attack from another character. A player can be moved directly by the user. From this prompt, we can generate the following code structure:
# object Location # attribute name # # object Character # attribute name # attribute location # # object Fighter inherits from Character # attribute health level # function attack(Character) # # object Player inherits from Fighter # function move(Location) # # object Foe inherits from Fighter # # object Friend inherits from Character # function defend(Character)
Once the goals of a project have been separated into different objects and groups of functions, they can be moved into individual files, which can then be imported and called from each other. In Python, the main file is traditionally called __init__.py, while other files are named based on their purpose. In general, each object definition should have its own file, unless several objects are directly related.
Each file you create should be properly documented with a comment at the top explaining what that file does. In particularly large projects, you may also want to organize your files into directories based on their different purposes. Files in different directories can also be imported from each other! An example of a project file structure can be found here.
# The main file, __init__.py # This file runs the whole game by calling playGame # In this directory, we have a file named heroClass.py, a file named monster.py, and a folder named gui which contains the file displayGame.py. We also have a folder named images where all assets are stored. import heroClass from monster import * from gui import displayGame def playGame(): # When we import a file normally, we have to reference that file name before # the functions/classes in it mainPlayer = heroClass.Hero("Fred") # When we use from file import *, all the functions/classes from that file # get imported directly into our current namespace! monsters = spawnMonsters() # We can also use from directory import file to organize files nicely displayGame.runGame(mainPlayer, monsters) - Algorithmic Planning
While planning, it also makes sense to start by identifying what the most algorithmically complex parts of the project will be. This can help you write out approaches for algorithms in pseudocode early on, so that you aren't stuck trying to code a complex algorithm from scratch near the deadline. Algorithmic plans can be fairly high-level, as they will mainly serve as a roadmap for later work. An example of a high-level plan is shown below:
######################################################### # Algorithmic plan for populating the field with monsters ######################################################### # # First: generate a set of monsters with varying attack levels # - number of monsters should be determined by field size # - level should be determined by hero level at beginning: half should be at # hero level, 1/4 one level above, 1/4 two levels above, and 1 five levels above # - distinguish monsters based on fill color? # # Second: determine where monsters should be placed on the field # - use random library to randomly place them # - but weight the placement so that easier monsters mostly appear closer to the hero # - non-randomly place the boss monster at the end # # Third: don't start monster movement until the gameplay starts # - have a class method to pause/unpause all monsters? or put a block in timerFired? #########################################################
In general, when planning algorithms early on, it's a good idea to use the problem-solving procedures we discussed earlier in the semester. This will help you tackle the difficult problems in your code. - Citing Code
Using code that already exists online is a great way to make an awesome project without having to do all the work from scratch! But it's HIGHLY important to cite any code that you get from an outside source. Doing otherwise counts as plagiarism, and can also violate copyright laws in some cases.
Where the citation is placed varies depending on how the code is used. If you're using a whole library, citations can be included in the readme/design docs of the project, to make it clear that this is a large contribution. If you're only using a small block of code (or even a single line), citations can be included in-line instead:
message = "Now we're thinking with colors!" # CITATION: I got the bcolors class from https://stackoverflow.com/questions/287871/print-in-terminal-with-colors-using-python class bcolors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' print(bcolors.HEADER + message + bcolors.ENDC)
Also, please remember that you should always cite ALL code that you get from an external resource, and that includes the course notes! You should basically always give credit where credit is due.
- Structural Planning
- Practical Concerns
- Version Control
When you're working on a large project, it is almost ESSENTIAL that you regularly create backups of your work. There is absolutely nothing worse than having your computer die and losing three solid weeks of work. Additionally, you'll sometimes find that you've added a bug to your code that you know didn't exist in a previous iteration, and you may find that you want to go back to the code you had before.
In this class, version control can be fairly simple. We recommend that you start by storing all of your term project code in your Google Drive provided by the university, since it gives you plenty of storage space. Then, if you ever need to recover work, you can right-click on the file, go to 'Manage Versions', and grab an older version from there. Emailing code to yourself also helps you keep track of work.
If you want to do more advanced version control, you can use Github, which gives you fine-grained control of the edits you make to your files. But be warned: it's possible to mess up with these forms of version control and accidentally delete all your files!
- Deliverables
Working on a three-week project can be intimidating, because it's not always clear how you should start. Organizing your work into deliverables can make it easier to break up your project into achievable tasks.
A deliverable is basically a smaller chunk of the project, like a feature or a single page of a GUI, which you can complete separate from the rest. If you can break your project up into deliverables, you can then set yourself due dates for those deliverables separate from the due dates in the course. Setting up a calendar of deliverable due dates at the beginning of your project will help you reduce your stress and get your project done on time!
- Version Control