This document introduces how to minimize the friction between contributors. It is mainly written for developers.
So if you are a new developer in our team, welcome! Please read carrefully do not hesitate to reach out if you want to discuss something about this document.
- coding style
- git / deploiement workflow
The code should follow the following guidelines:
https://github.com/bbatsov/clojure-style-guide
Mainly it states that the syntax should be the one provided by default with emacs. Most (every) developer use emacs right now to contribute.
The motivations for these guidelines are code readability and also management of code complexity:
- Select meaningful names for functions and variables. Avoid acronyms that aren’t commonly known.
- Prefer rewriting code to make it clear over adding comments. If that cannot be achieved, add comments.
- Limit the number of functions invoked in a single line of code to five.
- Limit the nested depth of a function definition to four. In order to achieve this it may be necessary to introduce local variables to hold intermediate results or extract code into additional function definitions.
- Only use anonymous functions for short function definitions that fit comfortably on a single line. Otherwise create a private, named function.
- Do not write anonymous functions that have arguments that are anonymous functions.
- Make an effort to limit the depth of the code. Mainly, you should reach a lib or core function as soon as possible while following the functions of a namespace. Other member of the team should not need to open more than three namespaces at a time to reach all the functions necessary to understand your code.
- Do not use loop/recur unless you can’t use reduce. Do not use reduce unless you can’t use a combination/threading of functions.
- Never use
defmacro
unless you really can’t use a function and every other member of the team couldn’t find a better way. - Never use
defmethod
. - Only use
defprotocol
for declaring TK services. Never use it unless you can’t do otherwise. Most of the time usingcase
and/orcond
will be preferable. - Try to avoid the use of
core.async
there is certainly an easier, simpler and less bug prone way to do what you intend to.
We work in a production environment. Once a code is in production, people are using in some way, we can’t break it.
Try to make your code upgrade proof. Inspired from (Spec-ulation talk of Rich Hickey https://www.youtube.com/watch?v=oyLBGkS5ICk). Ask yourself the questions:
- “Will we be able to upgrade without downtime?”
- “Will it be easy to put that new code in production without breaking the service?”
- “Will that change imply to run a data migration in production?”
- “Will that change break the workflow of any client?”
- “How easy will it be to upgrade the configuration?”
- “What will occur if the 3rd party change slightly their data format?”.
For example: notice that adding new fields to the configuration is not as expensive as modifying it or removing a key, because it will force that the deploiement of the conf and of the code to be synchronized. While if you can add new fields, you can upgrade the configuration before upgrading the code in production without any downtime.
- For most spec/schemas, make them as permissive as possible.
For example
(defschema Conf {:port s/Int})
is not as permissive as as(defschema Conf {:port s/Int s/Any s/Any})
. Because if the configuration change we would be able to add new fields to the configuration without breaking the running version. - Only add new fields, new values.
Because modifying the type of a value or removing a field will
break between different version of the code.
OK: (s/enum :a :b) => (s/enum :a :b :c) NOT OK: (s/enum :a :b :c) => (s/enum :a :b) OK: {:a s/Int} => {:a s/Int :b s/Bool} NOT OK: {:a s/Int} => {:a s/Bool}
In our team, GitHub is currently used both as a developer tool and as a task management tool. This document only focus about the developer tool aspect.
Our workflow is close to the Gitlab flow (see: https://about.gitlab.com/2014/09/29/gitlab-flow/)
- Clone the ctia repo in github to have your own clone say (where
johndoe
is your nick):https://github.com/johndoe/ctia
. - Add a
tg
remote for the mainthreatgrid/ctia
repository:git remote add tg https://github.com/threatgrid/ctia
./init-local-git-config.sh
You shall now have:
- a default git commit template
- an alias:
git synctg
that shall sync yourorigin/master
with thethreatgrid/master
correctly.
You can test everything is working correctly by doing:
> docker-compose -f containers/dev/docker-compose.yml up -d > lein test > lein run -m ctia.main
- You should have an open issue in github with number
#XXX
. git checkout -b issue-XXX-issue-short-description
- work… make a some
git commit
; the first time do not use the-m
option, then for all other commits, you shall use agit commit -m
with short messages (<50 char is best). - Optionally clean up your git log history by doing a
git synctg
and then agit rebase -i master
. Do not remove the first commit message with the Release block. - Test locally
./build/compile.sh && ./build/run-tests.sh && lein tk
git push -u
will push and create the branch on github- Open a PR. In the PR reference the issue, follow the instructions of the template.
- Make changes according to PR feedbacks
- Either use the
Squash & Merge button
in github or manually rebase.