Wednesday, April 18, 2012

ya ain't gonna need it until ya need it

Yesterday I posted a somewhat snarky comment about how You don't need layers until you need them which may have seemed like a nonsensical thing to say. Today I was started to write an example of how to refactor an anemic data model with lots'a layers into a lean and mean persistance machine... but stumbled into a perfect example of what I was trying to say. In essense, I was trying to repeat the idea that "Ya Ain't Gonna Need It", but with emphasis on the fact that... Yes, you may KNOW you're going to eventually need it, but building infrastructure before you need it accumulates overhead that you must pay for, even if you don't get the benefit.

My Example (Snippet of pom file)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <properties>
        <scala.version>2.7.7</scala.version>
        <spring.version>3.1.1.RELEASE</spring.version>
    </properties>
    <groupId>tstMaven</groupId>
    <artifactId>tstMaven</artifactId>
    <version>1.0</version>
    <dependencies>
        <dependency>
            <groupId>rome</groupId>
            <artifactId>rome</artifactId>
            <version>0.9</version>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-compiler</artifactId>
            <version>${scala.version}</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.1.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.1.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.1.1.RELEASE</version>
        </dependency>
    </dependencies>
</project>

What's wrong?

First, take a look at the scala version number. I've prematurely assumed I'm going to have multiple things that will depend on the scala version and moved it to a property. Don't do this, why? Because you don't need it. :) More importantly, if everyone follows this standard, they'll end up doing more work, one step to put the version property at the top of the file and another step to put the replaced string down in the dependencies section. Even more importantly, a person wanting to know which things have multiple dependencies on the version number will have no immediate cue as to which things have intentionally identical version numbers. The important theme is to try and communicate INTENT to subsequent developers.
Next, you'll note my spring config has the exact opposite problem, I've got three dependencies that all SHOULD move in lockstep and the version number is defined independently.
A lot of tech folks will immediately say "Just make everything use a property, that way it's all done the same way". I would agree, there is some value in standardizing on "how to define the version", but I think there is more value being lost in adopting this lowest common denominator mentality. In short, but only externalizing the version number when it's necessary, it adds a clear signal to the next person looking at the project when there are versions that multiple dependencies are dependent on.

The refactored version

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <properties>
        <spring.version>3.1.1.RELEASE</spring.version>
    </properties>
    <groupId>tstMaven</groupId>
    <artifactId>tstMaven</artifactId>
    <version>1.0</version>
    <dependencies>
        <dependency>
            <groupId>rome</groupId>
            <artifactId>rome</artifactId>
            <version>0.9</version>
        </dependency>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-compiler</artifactId>
            <version>2.7.7</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>
</project>
This version eliminates the problem of knowing which things must travel in lockstep version and which things can version independently. While this requires more thinking when building the pom file, and it certainly a trivial example of a larger problem, I think it illustrates what YAGNI really means. Unnecessary baggage should be though of as equipment you're putting in your backpack for a 1000 mile hike... sure it might be nice to carry 20 lbs of first aid equipment, "Just in case", but remember you've got to carry all that stuff every step for the next 1000 miles.

No comments: