(My) Dart Katas

Quite frankly, I think that this should actually better be named something along the lines of Dart Lab, the examples/exercises below are much more suited to expose experimentation than demonstration. But I won't change anything, if this is shameful, so be it!

The website CodeKata has a wonderful motivation text about programming katas, with which I squarely agree:

How do you get to be a great musician? It helps to know the theory, and to understand the mechanics of your instrument. It helps to have talent. But ultimately, greatness comes from practicing; applying the theory over and over again, using feedback to get better every time.

How do you get to be an All-Star sports person? Obviously fitness and talent help. But the great athletes spend hours and hours every day, practicing.

But in the software industry we take developers trained in the theory and throw them straight in to the deep-end, working on a project. It’s like taking a group of fit kids and telling them that they have four quarters to beat the Redskins (hey, we manage by objectives, right?). In software we do our practicing on the job, and that’s why we make mistakes on the job. We need to find ways of splitting the practice from the profession. We need practice sessions.

It is important to emphasize the fact that katas should be minimalistic units of practice so you can focus on just one topic. This will help us identify mistakes and univocally connect them to their root, with no ambiguity at all. Similarly to what Doug McIlroy mentions in the Bell System Technical Journal from 1978, albeit in a different context and with different intentions:

Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new "features".

Again, CodeKata has a nice description of what katas are:

What makes a good practice session? You need time without interruptions, and a simple thing you want to try. You need to try it as many times as it takes, and be comfortable making mistakes. You need to look for feedback each time so you can work to improve. There needs to be no pressure: this is why it is hard to practice in a project environment. It helps to keep it fun: make small steps forward when you can. Finally, you’ll recognize a good practice session because you’ll come out of it knowing more than when you went in.

Code Kata is an attempt to bring this element of practice to software development. A kata is an exercise in karate where you repeat a form many, many times, making little improvements in each. The intent behind code kata is similar. Each is a short exercise (perhaps 30 minutes to an hour long). Some involve programming, and can be coded in many different ways. Some are open ended, and involve thinking about the issues behind programming. These are unlikely to have a single correct answer. I add a new kata every week or so. Invest some time in your craft and try them.

Remember that the point of the kata is not arriving at a correct answer. The point is the stuff you learn along the way. The goal is the practice, not the solution.

Feedback on your craft is very important so you can do the movements or write the code correctly. As Vince Lombardi once famously said, again, in a different context and with different intentions:

Practice does not make perfect. Perfect practice makes perfect.

The Programming Kata's origins are unkown, but the earliest explicit mention comes from Dave "Pragmatic Dave" Thomas' book The Pragmatic Programmer (1999). This practice was further popularized by Robert "Uncle Bob" C. Martin on his blog, in an article called The Programming Dojo.

Personally, I first got in touch with it after reading Uncle Bob's books Clean Code and The Clean Coder, in which he not only mentions and recommends this practice but also puts a layer on top: TDD or Test-Driven Development. The TDD requirement for katas makes your thinking much more systematic, your program easier to read, and the overall code more organized and robust. It's more work and time, but the end-product makes the effort worthwhile.

After mentioning TDD, it's also worth citing Kent Beck, who is actually its inventor. Beck has written many books related to TDD and, specially in his TDD by Example, he goes on to formalize some coding exercises he uses to learn languages and continuously hone his skills. In reality, these are not only katas, but benchmarks, points of reference in terms of code quality, structure and his programming ability in a specific language. The most important of these TDD exercises or katas is the xUnit framework, a harness he created for implementing TDD into code, which has been implemented in many different languages.

My most current katas are the ones written in my Dart Katas Github repository. These are much longer than the usual expected time of 1 hour to 1 afternoon. On average, mine took an afternoon to a whole day. The main reason for this is that I was intentionally more careful with the structure of the code, featuring TDD while also trying to make it more clean and object-oriented.

Some will disagree with my code and I'm not here to say that it is the best ever or the most correct, I'm not really a 9 dan at coding. If you have any constructive criticism about my code, please do create an issue on Github about it. But, for now, I haven't been fully convinced about the wrongness of most of the practices I exposed in that repository.

One example of practice for which a much more experienced and overall much better programmer, Marcelo Glasberg, criticized me was, in his words, "my systematic obfuscation of the test answers". For example, in the Bowling Game Kata, I created a file called fixture_data.dart where I deposit all of the variables used as the test answers:

        final Map<Map<String, int>, int> rollsPinsAndScores = {
  gutterGame: 0,
  allOnes: 20,
  perfectGame: 300,
};

const Map<String, int> gutterGame = {
  'rolls': 20,
  'pins': 0,
};

const Map<String, int> allOnes = {
  'rolls': 20,
  'pins': 1,
};

const Map<String, int> perfectGame = {
  'rolls': 12,
  'pins': 10,
};
      

His critique was that this was incredibly unnecessary since it doesn't really add much to so simple a program/problem. He also mentioned that it makes it harder for other people to read my code because they now have to manage more files and screens in their heads. I mostly do agree with his criticism actually, but I left it that way because it serves to show what I would do if the program got a little bit more unwieldy; I do believe, though, that separating those variables helps the reader understand what's being tested on a higher level of thinking and, if he wants to, he can examine the test data later on.

Putting the maps used in each separate test scenario inside another map was yet another source of his dislike of my code. In this case, I was trying to avoid the needless repetition of the test setups, which always had the same heuristics. I still think this makes the reader better think at a higher level more easily though. Rote repetition of simpler tests might be easier to read, but its volume will be harder to memorize and visualize as a whole. Instead of setting up the same types of tests over and over, you can simply use a forEach:

        test('Test canonical games: gutter, all ones and perfect', () {
  rollsPinsAndScores.forEach((Map<String, int> rollsPins, int score) {
    _game = BowlingGame();
    _rollManyPins(rollsPins['rolls'], rollsPins['pins']);

    expect(_game.score(), score);
  });
});
      

Another last warning I must give about my kata code in this particular repository is that I sometimes overparameterized or overgeneralized the problem too much — for practicing purposes —, which is a very easy trap to fall into for programmers. For example, my Tic-Tac-Toe solution can work for any symbol/character and any board size. Not only the board class but also the parser's would be much simpler if I hadn't chosen to implement this overgeneralized solution.

At any rate, in the end, I'll let you be the judge of what practice is best — if there is one thing I've learned from Go (Baduk or Weiqi) is that the discussion itself can be much more enlightening than simply labeling a practice as good or bad. Just keep in mind that, most likely, the circumstances will be stronger than your personal or supposedly general preferences.