NOTE: This document was originally written when this project was written in C++. While the general "spirit" of this document still applies, it needs to be updated to reference haskell things (i.e. haddock instead of doxygen).
In general, we want your code, some come on over!
- Work Flow
- Code Documentation
- Header Files
- [Test-Driven Development (TDD)][#test-driven-development-tdd]
- TODO File
- Licencing New Files
Work Flow ↑
NOTE: I had a nicely updated Work Flow section made up - and then I borked it up with a
silly git rebase --hard
trying to get fancy with my history re-writes.
Long story short:
- Always start with an issue (in gitlab)
- Use the TDD methodolgy outline in this document
- Use a merge-request to get your code added to master.
One of the User Stories listed in [UserStories][UserStories.md] should be used as the starting point for most (alL?) new features.
the information gleaned here, here, and here were referenced in creating this document, and some others.
to be continued... (need to recover the last info...just rewrite it.)
Code Documentation ↑
The header file should contain doxygen-style code documentation. However, the documentation present in the header should be somewhat minimal. The header documentation can and should include:
- A brief description of the class/method/function
- Documentation for the parameters
- Documentation for what is returned
Any more detailed description, including documentation of exceptions raised, should go into the source file.
This should provide a decent balance between readability of the header file (which is more likely to be references than the source code) while still provide as much detail in the documentation as is needed.
Header Files ↑
Any header files which describe the public api should be placed in the top-level include
directory. Any headers which describe purely "internal" constructs, i.e. stuff that users
don't need to worry about, can be placed somewhere in the src
tree.
Please note that a "user" here could be an end-user using the software, or a developer using our code as a library. Keep this in mind when choosing where to put things.
Test-Driven Development (TDD) ↑
As much as feasible, a Test-Driven Development methodology should be employed. In other words, the development cycle should go something like this:
- Write a test that fails (compilation error is a failure)
- Write the least amount of code needed to make it pass
- Refactor the code to make it...right
I'll expound on these a bit, but honestly I am no expert.
Writing Tests ↑
Tests should be minimal - in other words, consider a feature or behaviour that you need the program to perform. Now write a test that only tests that feature or behaviour.
Here's an example - let's say we're writing a calculator program. That's a big task. It's difficult to write a succinct test that "calculates", because there are so many variables to consider.
Instead, start at step one - we need to be able to provide numbers to the calculator.
So, our first test might be testGiveDigits
and some pseudocode for the test might look
like:
Calculator::giveDigits("123");
assert(Calculator::getDigits(), "123");
This will fail for many reasons: first, there is no class defined which is called Calculator. Next, even if there were, then it wouldn't have the requested methods.
Writing code: first pass ↑
Apparently, we're supposed to forget everythnig we know about good coding practices etc. here and "just make the test pass!" So let's try that:
#include <string>
namespace Calculator
{
std::string myDigits;
giveDigits(const std::string& numbs)
{
myDigits = numbs;
}
std::string getDigits()
{
return myDigits;
}
}
If we try this out, I think it will compile. What's more, I think our test wil pass!!!
Writing Code: second pass ↑
Now that we have a passing test (phew!), let's take a step back and observe our creation. We're using a global variable. Yuck. let's go back and refactor this a bit in order to use some decent coding practices.
#include <string>
class Calculator
{
public:
static giveDigits(const std::string& numbs)
{
myDigits = numbs;
}
const std::string& getDigits() const
{
return myDigits;
}
private:
static std::string myDigits;
}
This is a bit better. We got rid of some std::string
copying that was going on, and we
encapsulated the myDigits
variable in a class. The class is purely static, which doesn't
feel great, but I guess until the tests require us to change it, we'll just leave it
alone...
Write More Tests ↑
As far as I can tell, that's really the gist of TDD. Now just rinse, lather and repeat.
I've actually had limited success using this type of approach, but maybe it's because I don't really know what I'm doing. Let's try to use this disciplined, structured approach, in order to ensure that our code-base is bulletproof!
TODO File ↑
A TODO
will always be present at the top-level of this repository. Given the nature of
how we're approaching TDD, first-passes at writing code can and will likely lead to bad
code. In fact, you may find yourself writing something like:
float MyClass::calculate() const
{
return 20;
}
Because you know that this will force the unit-test you wrote earlier to pass. This is
ok - future unit-tests (motivated by the desire for the calculate function to do more than
just return 20
) will force us to change this.
However, for something as blatantly wrong as this, it should be noted in the TODO
file
so that we don't lose track of it.
The TODO
file is not intended to grow forever. Rather, as we enter the "second pass"
of our TDD cycle, we should address some of our issues in the TODO
file, and erase them
from the TODO
file as they are resolved.
It is OK if you leave something in TODO
for a few cycles.
It is not ok to leave somthing in TODO
forever - either do it, or don't.
Licencing New Files ↑
The following has been taken directly from the GNU AGPLv3. Any new source files should contain, at the least, the two lines listed, though preferably the entire block, as the first lines in the file.
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements.
You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see https://www.gnu.org/licenses/.