Tuesday, September 16, 2014

Reactive Programming

This is hard for me to explain yet, but here are helpful links:

Basically, state is passed down, and events go up. Component A creates Component B, passing parts of A into B as immutable properties, and setting call-backs on B to call into A. Most components are re-created repeatedly.

  • Data-flow is clear and consistent.
  • Mutable state is isloated and minimal.
  • The entire architecture is highly scalable.

Friday, September 12, 2014

Git: Rebase, then merge (--no-ff)

In case you think you should always either rebase or merge, check out this reddit post.

And this blog shows how to rebase a stale GitHub "pull request" (as opposed to using the "Merge Pull Request" button).

C++: copy elision

The C++ standard permits the compiler to skip (or "elide") a constructor under some circumstances, e.g. when a temporary is passed into a function by value. There is plenty of information on the web about this subject, but here is a concrete example.

What's interesting is that "copy elision" can actually alter semantics:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects.
Anyway, because of this feature, it can be wise to let a method accept an input by value rather than by const-reference, e.g.
table[key] = value;
The pattern should be considered whenever the method would have created an explicit copy, as for swapping.

Friday, August 15, 2014

Recursive memoization with Go

There are many odd solutions on the web to the "Web Crawler" exercise in the GoLang tutorial:

It's presented as a recursive function which requires memoization and parallelism. There are 2 main approaches:
  1. Synchronization primitives, or
  2. Callbacks
Both are needlessly complex. Here is my solution:
Mine is simple because I dropped the recursion. If you see a producer/consumer pattern with recursion, you probably need synchronization primitives. But if you can drop the recursion, then the consumer can know exactly how many producers he created.

An interesting sidebar is how to memoize in Go.  A closure is probably the best way, but I didn't bother with that in my solution, to minimize diffs.

For another angle on the deceptive simplicity of gochannels, read this:

Friday, July 4, 2014

Storing dotfiles in Git: symlink + worktree

I use 3 tricks:

  1. core.worktree = /Users/cdunn
  2. symlink .git -> dotfiles/.git/
  3. In .gitignore: *

The beauty of my approach compared to many others is that regular git commands work directly in my home-directory. It's easier to explain with an example:
To add new ones, pass the '-f' flag to 'git add'.

Saturday, April 19, 2014

The Evil Unit Test

 (This is copied from the blog by Alberto Gutierrez, posted January 27th, 2012 at 7:54 am, on makinggoodsoftware.com, which recently vanished. I pulled this from the Google cache. I largely agree. ~cd)

(Addendum: I had a conversation on this point with Earl Everett, after his engaging talk at Agile Austin. The point of contention was whether an internal class can be considered a "product" to another developer. Think of an algorithm which you will present to others. You will test the algorithm thoroughly, with full branch coverage etc. Do you need to "unit-test" the underlying classes that were used to implement the algorithm?)

The evil unit test.

Written by Alberto Gutierrez

Unit tests can be evil, I know that sounds harsh, but I think there is a battle to be fought to make people understand that unit tests are not always good, and that they actually can become evil, specially when overused.
Think in a foreach, most languages have an implementation of this flow statement. Is the foreach a useful utility for programmers? Definitely! What about function pointers? or Design patterns? or ORM frameworks?… All of them are useful utilities, but can you imagine a programmer, or a book naming the following principles?
  • You shall use a foreach every time you need to perform a loop.
  • Every third method shall implement a design pattern.
  • You will never access your database if you are not using an ORM.
Stupid, right? So why is it that there are programmers happy with statements like:
  • Every public method shall have a unit test.
  • Unit tests are not to be deleted. There is always a reason for a unit test to exist!
  • Unit tests must be created before the code they test.
  • Unit test coverage should be 100%.
Unit tests, as any other utility, tool, principle, philosophy etc. are only to be used when applicable and convenient, and not to be taken as religious dogmas. I have already made this point in my previous article, but since it generated so much controversy I thought it would be good to expand on it. If you can’t unit test a piece of code from a behaviour/black box perspective, then you probably shouldn’t write a unit test at all. Your unit test should check what your code is doing, not how. If you create a unit test that it only focus on how your code is, instead of what it does, then you are writing an evil test because:

1. Evil tests create a lock on how the code is implemented.

Every time the implementation of the method changes, even if its behaviour is maintained, the test becomes red. There is no reason to lock the implementation, because there is no advantage on doing so. If there is some code that is especially tricky, locking its implementation with tests won’t make it easier to understand or won’t force anyone to think twice before changing it.

2. Cause duplication.

The beauty of code with good unit tests is that they compliment each other; the code to be tested represents the implementation (the how), and the test guarantees behaviour (the what). On the other hand, evil unit tests look again into the how, so you will have two places where you state how do you think a problem should be solved, only that you will be using different expressions to do so. Is like saying: “2*3 = 2 + 2 + 2”, and then writing a test that says “Guarantee that 2*3 is the addition of 2 three times”, as opposite to “Guarantee that 2*3 is 6”. Testing the implementation and not the behaviour is a waste of time and serves no purpose since you can always go to the code to find what the implementation is.

3. Builds uncertainty on the tests (red is meaningless).

One of the main advantages of having an automated test suite is that feedback loops become shorter. It takes less time from creating a bug to detecting it. When the code gets polluted with bad unit tests, the automated tests seem to start getting broken randomly and this sometimes leads to a point where developers don’t care much anymore about having their tests passing as usual as they should.

4. Decrease productivity.

Given all the circumstances mentioned already, developers dealing with evil unit tests are faced with many inconveniences that forces them to expend time in unproductive tasks, like refactoring useless unit tests, writing them…

5. Discourage change.

Code bloated with bad unit tests is a nightmare to maintain, you make an implementation change, not a behaviour change, and you have to expend ages fixing tests, hence you minimize your implementation changes which is the core idea behind refactoring.
Consider the following principles then:
1. Delete evil tests. There seems to be a taboo around deleting unit tests, is like they are untouchables, well that’s no true, if the test is not adding value, then the test is an evil test and MUST be deleted.
2. Minimise the areas of your code that can’t be effectively unit tested. It is also true that sometimes the problem is that the code is written so that areas that should be unit testable are tied to components that make difficult the unit testing, in this case, the programmers needs to expend more time understanding the principles behind loose coupling and separation of concerns.
3. Write integrations tests for areas that are not unit testable. There is always going to be code in your application that is going to be legitimately not unit testable, for this code, simply don’t write a unit test, write an integration test.