A set of exercises I did to learn Go lang.
In order to learn a bit of Go, I decided to do so by solving a few different problems from the Euler problem archive.
To make it more interesting, I also trialed using the Commit and test or revert approach as explained here by Kent Beck.
Halfway through it I decided to move off Euler's problems in favour of something more practical. This was mostly due to the problems being repetitively dealing with integer math, and string manipulation.
In order to have a challenge closer to real work, I opted to create:
Upon initial inspection of Go, I noticed a few aspects that I expected to prove challenging to my background. Mostly:
- No generics
- Interfaces implicitly implemented
- Go routines and channels
- Separation of code and data (structs and extension functions)
Other challenges found that I wasn't expecting are:
- Go expects your code to be in a very specific structure. This is best explained here.
- Didn't use an IDE while writing the code. Eventually switched to Goland trial version.
- Go is quite strict on its warnings. Thigs like unused variables/imports, or non standard formatting will trigger a build failure. This made me lose progress several times due to the test && commit || revert approach.
- Lack of knowledge of Go's syntax, Go being strict on warnings, and the Commit and test or revrt approach, meant I wasted many iterations due to simple mistakes a syntax highlighter would have solved. Switched to Goland in a trial account.
- Value vs reference type. Despite knowing the asterisk syntax from C/C++ I was confused by the fact you can implement an interface with reference semantics to mutate the object, while value types won't mutate it. See change here. Article for reference here.
- Uppercase for public, lowercase for private. Initially I thought it was following .Net's uppercasing, but quickly noticed it was not the case when I started using multiple namespaces. Using
package %PACKAGE%_test
for the test files was very helpful to help with encapsulation. See this article.
Initially I went with a strict approach, which led to skipping the red phase of the Red-Green-Refactor cycle. Many times I ended up being frustrated at losing code that was 90% correct, but had a typo, or wrong formatting.
As a result I ended up switching to having two approaches in my makefile:
make green
- Test && Commit || Revert, used when I expect a green state.make red
- Test && Revert || Revert, used when I expect a red state.
I sticked with it for the sake of evaluating it, but in conclusion I think that not reverting the changes would be a more appropriate approach for the way I'm used to work.
I wouldn't apply this approach in a production codebase because:
- You need to run from a terminal, breaking the flow you have on your IDE.
- The commit message being autogenerated would lead to garbage history in git. Can check the history of this repo to see how it's completely insufficient to understand what I was working on.
On the other hand, a git pre-commit or pre-push. hook to run your unit tests and ensures you don't push code to the remote would mimick well enough the desired outcome. The downside of this approach would be that committing would become an expensive operation if tests last too long, which would entice developers into not committing as frequently as we should.
Summary
In summary, I believe this technique to be a great learning technique to incorporate into katas and pair programming training sessions, but I struggl to see this as a production methodology.