How To Collaborate Code Effectively

Mike Nöthiger
11 min readJul 26, 2019

Writing code is like art, so is the process of collaboration. You can always seek to improve and become a better collaborator. This article is about what I have learned during my time as a code collaborator. Not everything may hold true for you. It may not even hold true for myself, because I try to constantly evolve my own experiences. Rather it represents a snapshot of my experience and you can take out whatever’s useful to you and throw away the rest.

When writing code, we spend a lot of time thinking. I often get lost in my head from all that thinking. It’s essential to know what you are working on. This is why I keep myself close to the issues I’m working on. They remind me of what I’m truly supposed to solve. You can make a branch for every issue to help you stay on track. Name the branch after the issue‘s ID, this way you increase the traceability for you and all collaborators. Only add code to this branch that is related to your issue. Go back to your issue and read the specification if you get lost somehow. I catch myself so often, trying to solve a problem that is not even relevant for what I was initially supposed to solve. It’s a waste of time.

If you get stuck on an issue, start to write down what you’re stuck on. You can add it to your issue description/comment section. Don’t care about correctness or formulation, because it will be hard to explain a problem when you’re not exactly sure what the problem or its cause is. But that is exactly why it will help you. It pulls you out of your head and breaks the circling thought process. Words give the problem a shape. This shape is much easier to grasp. As soon as you have a shape you can enhance and deform it. It gives you new space for thinking in your head. It’s like emptying your RAM by writing it to the hard disk. Trying to write down what my actual problem was, helped me so often to find an actual solution for that problem. As a nice side effect, you inform anybody else about what you’re stuck on and document the state of work. You’re now even able to interrupt your work, start something else and come back later to continue on your problem. Continuing on the problem will then be much easier, because you created neuronal connections in your brain as you have written down the problem, those connections now help you to remember where you left work.

Write test driven code. Seriously. I can tell you how that works for me. It always starts with the idea. I have this idea on how to solve a problem, like an intuition. So I start creating some classes/functions, like a blueprint of what I have in my mind. But then I stop, I don’t implement anything specific, because in my mind I have this idea of how my code behaves, not how it is implemented. What I do next, is I write a test that exactly expresses my idea of the code behavior. Now you’re left with a crystal clear, machine readable evaluation of what you had in your mind. Once again you gave an idea in your head a shape. Other people will better understand what you had in your mind and machines can verify, whether the implementation, which starts from now on, fulfills the expectations. You have basically written an automated evaluation of your idea which is truly valuable. It’s essential to do this process at the time of your idea and not weeks, days or even hours later. Because your idea will get blurry over time even for yourself. Furthermore, I often find missing puzzles of my concept during the creation of my unit test because now I’m forced to precisely write down how my code must behave.

Commit often. This will decrease the risk for code divergence. When code diverges, you will end up with merge conflicts. The more code diverged, the harder it will become to resolve the conflicts. The risk for code divergence increases as time passes between your commits as well as the number of collaborators increases. Try to develop a habit for short commit cycles. A few things can help you shorten the commit cycles:

  • Only work on what you’re supposed to be working on.
  • Always be aware of what your are working on.
  • Lock yourself into a branch that is solely dedicated to what you are working on, which will support you to stay on track.
  • Commit every time, when your code is in a runnable state.
  • Work on your commit messages. Try to express exactly the things you have done. If you have done much, then your commit message becomes longer. Use your commit message as a metric to measure the commit size. Too long commit messages indicate that you should shorten your commit cycles.

Together with the need for short commit cycles comes the need for short merge cycles. Merge your branches often into the master branch, otherwise you’ll be left with code divergence and merge conflicts again. This entails short branch lifecycles, consequently a branch should not cover large work loads. I usually make a branch for an issue. An issue should not take more than a day or two to finish. Of course there are exceptions to this rule; if an issue takes a whole week or even longer you should consider merging it before it is completely finished. Facilitate feature toggles if you want to merge but actually hide the feature you are implementing from the end user.

Decrease the potential points of failure by automating repetitive tasks. Repetitive tasks are especially prone as a source of failure. At the same time they are perfectly suited to be executed by a machine due to the repetitive nature. Machines perform best if they have to do the same thing over and over again. Common repetitive tasks are:

  • Run tests.
  • Build artifacts and containers.
  • Publish artifacts and containers.
  • Deploy an application.
  • Increment versions.

These tasks can be automated so easy with the tools available today. GitLab CI/CD is like: “What do you want me to do with your code when somebody commits?”. You’re like: “Hmm, well, let’s run my tests which I would do anyways”, so GitLab is like “How are you usually running your tests then?” and you’re like “gradle test” and GitLab’s like:

image: gradle:5.4

stages:
- test
test:
stage: test
script:
- gradle test

Of course GitLab is just one example. I feel confident that all other tools available out there gear you with the same tools and quality, so make use of them. I was often told, that a CI/CD pipeline is useless as long as it does not run through successfully. This is completely true but it’s most often owed to the desire for too fancy and far too advanced tasks included into the pipeline from the very beginning. Start simple, with things like compiling the code. It’s the very small things that can turn out very valuable on the long run if they constantly perform well in what they are supposed to do. As soon as you have a working pipeline, you can start to enhance it, step by step. Add things like running unit tests and building artifacts then go on to more complex tasks like running UI tests or deploy the application. If something does not work properly, you can always go back to the old CI/CD configuration that has worked well.

Don‘t waste too much time on technology evaluation. There are so many tools out there and they all do a perfect job. In fact, competition is so hard that nobody can afford it to provide insufficient quality . Just pick one, learn it and stick to it. You will always detect some flaws but that is no reason to throw it all away and change the whole technology stack (just to land at the very same point a few weeks later). It‘s like with persons, the true value unfolds when you start accepting their flaws. Never forget that tools should be supportive and not required to achieve a goal. We easily tend to think we can‘t achieve a goal if we don‘t have the right technology. While that can hold true for some rare occasions, it‘s most often an illusion caused by technology marketing. When technology becomes the problem you want to solve, it’s a clear indicator that you went way too far down the technology road.

Don‘t be too radical with new ideas. While I‘m a huge fan of learning and improving, it‘s not the best option to completely replace common experience with something that was newly learned. You are often aware of the flaws about the way you are solving a problem right now, but this way of solving the problem has at least passed enough validation to be actually used. It works the same with books; when they come out new it is very hard to pick the valuable ones. As time passes, irrelevant books disappear while the truly valuable ones stay on the market for decades, centuries if not milleniums! New ideas were barely validated yet, this can easily end up in a trap where you make things worse rather than improving them. Try to find a middle between common experience and new ideas/learnings. Let time pass and validate this new idea. If your expectations hold true, you can constantly shift more towards the new idea. If not, you can turn back towards the old approach, without being frustrated about walking around in a circle.

Document the same way you code. Everybody knows “the zone”, this flow you sometimes get in while programming. The lines of code literally spill out of your mind, through your fingers, into the computer. You couldn’t tell what the single lines of code are doing but you certainly know they all contribute to the big picture you have in your mind. Documentation should become part of this flow. Your system is much more than just the code. Many thought processes took place until code arose. Those thought processes are essential to understand your system. You might think everything can be deduced by looking at the code but that’s a strong fallacy. Leaving information implicit gives too much room for interpretation. Varying interpretations of your system will arise across the collaborators, which consequently leads to misunderstandings. The best way to prevent misunderstandings and seek for consent is to make the implicit explicit. Call the things by their name. Write (or draw) down how you meant things. This will allow others to confirm it or give feedback about a different interpretation.

Think of your system as something that (at any point in time) has to be picked up, adapted and extended as quick as possible. While coding, you need to get a feeling for “what is going to be important in order to understand the things, that I’m currently working?”. I have had many projects, where I failed on this part. It’s so hard to distinguish the obvious information from the not so obvious information. When you’re in the middle of something, everything seems to be obvious. Coming back two weeks later or even months and you will realize that not even half the things were that obvious as you thought (which is actually one of the best learning opportunities to improve your feeling for “when to document something”). Now imagine how other collaborators feel about your work. I started to switch to my documentation as often as I switch between files in my code. Sometimes, only to change a single sentence. The documentation should grow, evolve and get refactored the same way as the code does. In your documentation, you can write down everything that can’t be expressed with code. Words and images give you more room for expression, thus filling the missing puzzles to understand your system. Common things I document are:

  • Architectural approaches.
  • Learnings & Troubleshooting.
  • Best practices, specific to the project.
  • Common routines (build, deploy, etc.)
  • Usage examples that include multiple parts of your system.
  • Workflows and processes that should be adhered to (e.g. Merge Requests)

The easiest and most straightforward way to start documenting is a readme.md file. Markdown allows for simple text formatting and a readme can be tracked in your version control system (VCS). Furthermore, you can easily switch to the readme in your IDE. Every new project I start has a readme from the very beginning, even if it’s empty. As documentation grows, a wiki system or multiple *.md files might come in more handy.

When documentation gets integrated closely into your development workflow, you will start to evolve that feeling for when you have to document something or when you can improve existing documentation. Sometimes, when I get stuck at formulating my thoughts, because the idea is not that mature yet (or it’s simply the wrong day), I just write it down as good as possible. I can guarantee you, the idea will mature over time and a moment will come where you perfectly know how to express it (that’s such a beautiful moment of harmony). But it’s essential to plant the seed, to write down that initial, sketchy thought, otherwise there’s nothing to evolve. Documenting is another way of giving thoughts a shape, and thoughts are the building blocks of any software.

I want to end this article by sharing with you a pattern that constantly showed up in my development career. I call it the process of materialization and I claim it to be the cause for progress. The process of materialization always starts with an idea. We do not know how ideas arise but we’re thankful to have the ability to come up with ideas. An idea is blurry and very volatile and an idea, as long as it stays in our mind, is worthless. This is where the whole process begins. Everything we do from now on is to give this blurry idea a more specific shape. We materialize it. There’s a gradient of materialization which starts at the idea and goes way down to a very concrete expression of that idea, for example a mathematical formula. This gradient could look as follows:

  1. Idea
  2. Images, drawings
  3. Words (natural language)
  4. Machine readable code
  5. Mathematical Formula

As you go down this gradient, the idea becomes more physical, useful and valuable. Something that was only available to you, now assumes a form and can be shared with others. Common understanding and consent emerges. Collaboration starts and other people can now contribute to the process of materialization. Arriving on the level of code, the idea can even be understood and validated by a computer. It can be reproduced and integrated with other computer systems. During materialization we automatically validate the initial idea. We learn from failed validation and new ideas will arise, which will pass through the cycle of materialization again. If an idea is inconsistent at its very root, I can assure you it is only a matter of time until everything collapses that is built on top of this idea. This is why we should not hold on to invalidated ideas, rather should we try to invalidate them as fast as possible. Your system evolves by the number of materialization cycles you pass through. Common tools to materialize are:

  1. Talk about the idea
  2. Draw a picture of the idea
  3. Write down the idea
  4. Model the idea (e.g. with UML)
  5. Program your idea
  6. Express your idea mathematically

Sometimes you can skip multiple stages and go directly from an idea to programming but it tendentially gets harder to express something the more concrete you have to be about it. While I can easily talk about an idea, I will probably struggle to express it in an UML diagram.

Passing through the cycle of materialization as often as possible in our daily work life, is what we should seek for. It’s the fastest way how progress can emerge. If materialization stops, so does the progress. This is why I encourage to use all the tools available, to pull out ideas as fast as possible out of your mind and validate them down on earth. Seeds won’t grow if not planted into earth and provided with energy from the sun, nutrients and water, so it is with our ideas.

--

--

Mike Nöthiger

Hi! 👋 I’m Mike — did you know the oldest computer was owned by Adam and Eve? It was an apple with very limited memory. Just one byte and everything crashed.