Python for the busy Java Developer

For the past few months, I've been working on a book titled, Python for the busy Java Developer. I'm happy to say that it is finally complete and ready to share. Go, download it now!

Python for the busy Java Developer book cover

The book's page has all the details on the audience and the content of the book. So let me use this post to talk a bit about the experience of writing it.

It is a relatively short book and yet, the amount of effort that it took to finish it was immense. My respect for authors has gone through the roof! It is one thing to have a collection of topics to write about and quite a different thing to put all of those topics together into a cohesive narrative. I learned a great deal about structuring content, striking the balance between too little and too much detail and writing with the reader in mind. I also learned a few new things about Python along the way!

Beyond the content, creating a book involves publishing it in a format suitable for wider consumption. Traditionally, this is the bit that is handled by book publishers. However, since I didn't intend to approach a publisher, I had to figure out how to produce the book myself.

Initially, I started composing the book using the Markdown format and generating output using the Pandoc tool. However, I soon hit the limits of what Markdown is good for. There are several elements like admonitions and callouts in code samples that can't be expressed in Markdown. At this point, Hitesh pointed me to the AsciiDoc format, specifically the Asciidoctor project. This was a revelation!

The AsciiDoc markup format supports pretty much anything you would want to do when writing a book. The Asciidoctor toolchain makes it easy to produce great looking output from AsciiDoc source. There are still a few bugs and gaps but I am confident that in six months time, this will be the best toolchain for creating books.

Writing this book has consumed many evenings (and early mornings!) but it was well worth the effort for the experience. I hope the end result will be useful for the intended audience!

Team Geek

I just finished reading Team Geek: A Software Developer's Guide to Working Well with Others by Brian W. Fitzpatrick and Ben Collins-Sussman. The authors bring their experience of working on open source project teams (Subversion) as well as corporate software development teams (Google) and share what it takes to build great teams, create organizational change and manage your own career.

Although at times I felt that the book got a bit repetitive and could have done with tighter editing, overall, it is a great read with lots of hard earned wisdom on working in software teams. I've pulled out a few quotes which I particularly liked.

On not criticizing every single decision and learning to pick the right battles to fight:

Every time a decision is made, it’s like a train coming through town — when you jump in front of the train to stop it you slow the train down and potentially annoy the engineer driving the train. A new train comes by every 15 minutes, and if you jump in front of every train, not only do you spend a lot of your time stopping trains, but eventually one of the engineers driving the train is going to get mad enough to run right over you. So, while it’s OK to jump in front of some trains, pick and choose the ones you want to stop to make sure you’re only stopping the trains that really matter. -- Team Geek

On not tolerating people that threaten to poison your team's culture, even if they are "genius" programmers:

Genius is such a commodity these days that it’s not acceptable to be an eccentric any more. -- Team Geek

On attempting to change bad habits and behaviours:

It’s impossible to simply stop a bad habit; you need to replace it with a good habit. -- Team Geek

On learning to Manage Upward:

Shipping things gives you credibility, reputation, and political capital more than just about anything else in a company. -- Team Geek

On writing emails that get results:

A good Three Bullets and a Call to Action email contains (at most) three bullet points detailing the issue at hand, and one — and only one — call to action. That’s it — nothing more. -- Team Geek

On doing the Right Thing:

Do the right thing, wait to get fired. -- Chade-Meng Tan

Metrics reporting from Guava Caches

Codahale Metrics is a wonderful library to collect and report various types of metrics from your Java server-side applications. Google Guava is the swiss army knife of libraries that every Java developer should have in their toolbox.

If you use both these libraries, then at some point of time, you may find the need to capture statistics from Guava Caches and report them via Metrics. When you do, perhaps this class would be useful.

package net.antrix.utils;

import static com.codahale.metrics.MetricRegistry.name;

import java.util.HashMap;
import java.util.Map;

import com.codahale.metrics.Gauge;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricSet;
import com.google.common.cache.Cache;

public class GuavaCacheMetrics extends HashMap< String, Metric > implements MetricSet {

    /**
     * Wraps the provided Guava cache's statistics into Gauges suitable for reporting via Codahale Metrics
     * <p/>
     * The returned MetricSet is suitable for registration with a MetricRegistry like so:
     * <p/>
     * <code>registry.registerAll( GuavaCacheMetrics.metricsFor( "MyCache", cache ) );</code>
     * 
     * @param cacheName This will be prefixed to all the reported metrics
     * @param cache The cache from which to report the statistics
     * @return MetricSet suitable for registration with a MetricRegistry
     */
    public static MetricSet metricsFor( String cacheName, final Cache cache ) {

        GuavaCacheMetrics metrics = new GuavaCacheMetrics();

        metrics.put( name( cacheName, "hitRate" ), new Gauge< Double >() {
            @Override
            public Double getValue() {
                return cache.stats().hitRate();
            }
        } );

        metrics.put( name( cacheName, "hitCount" ), new Gauge< Long >() {
            @Override
            public Long getValue() {
                return cache.stats().hitCount();
            }
        } );

        metrics.put( name( cacheName, "missCount" ), new Gauge< Long >() {
            @Override
            public Long getValue() {
                return cache.stats().missCount();
            }
        } );

        metrics.put( name( cacheName, "loadExceptionCount" ), new Gauge< Long >() {
            @Override
            public Long getValue() {
                return cache.stats().loadExceptionCount();
            }
        } );

        metrics.put( name( cacheName, "evictionCount" ), new Gauge< Long >() {
            @Override
            public Long getValue() {
                return cache.stats().evictionCount();
            }
        } );

        return metrics;
    }

    private GuavaCacheMetrics() {
    }

    @Override
    public Map< String, Metric > getMetrics() {
        return this;
    }

}

And here's a Spock specification that verifies that it works.

package net.antrix.utils

import com.codahale.metrics.MetricRegistry
import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import spock.lang.Specification

class GuavaCacheMetricsTest extends Specification {

    def "ensure guava cache metrics are reported to the registry"() {

        given: "a guava cache registered with a Metrics registry"

            def cache = CacheBuilder.newBuilder().recordStats().build()
            def registry = new MetricRegistry()
            registry.registerAll(GuavaCacheMetrics.metricsFor("MyCache", cache))

        when: "various read/write operations are performed on the cache"

            cache.put("k1", "v1")
            cache.put("k2", "v2")

            cache.getIfPresent("k1")
            cache.getIfPresent("k2")
            cache.getIfPresent("k3")

            cache.get("k4", { "v4" })

            try {
                cache.get "k5", {
                    throw new Exception()
                }
            } catch (Exception expected) {
            }

        then: "the metrics registry records them correctly"

            def gauges = registry.gauges

            2 == gauges["MyCache.hitCount"].value
            3 == gauges["MyCache.missCount"].value
            0.4 == gauges["MyCache.hitRate"].value
            1 == gauges["MyCache.loadExceptionCount"].value
            0 == gauges["MyCache.evictionCount"].value
    }
}

[x] driven development

I made a new site: [x] driven development.

Go check it out!

The idea for this site came from a conversation at work where we were discussing design choices for a requirement. The designs felt like over-engineering and in a moment of frustration I said, "This feels like audit driven development!" We had a good laugh and then went back to hashing out a design that would satisfy Audit while still meeting the requirements.

That conversation stayed in my head and eventually, took the form of this new site.

Incidentally, the development of this site itself was a case of résumé driven development! I'd been reading about RethinkDB for a while now and was looking for a project in which to try it out. So when starting development on this site, I dived right in and coded the first version of this site using the Flask web framework and a RethinkDB based storage layer. Thankfully, sanity prevailed soon enough and I converted the site into a 100% static website. There's a bit of AJAX going on when you navigate around the site but that's still all served up as static content. Nothing fancy though, the few dozen lines of source will give you the complete behind the scenes story.

If you liked the site, you can subscribe to the feed using Google Reader your favourite news reader or get updates from @devdrivenby on Twitter.